Exemple #1
0
        /// <summary>
        /// This method converts a Neo tx to a Rosetta tx. Obsolete tx type will not be converted.
        /// Put utxo into operations. Put tx type and type specific info into metadata.
        /// </summary>
        /// <param name="neoTx"></param>
        /// <returns></returns>
        private Transaction ConvertTx(NeoTransaction neoTx)
        {
            Dictionary <string, JObject> metadata = new Dictionary <string, JObject>();
            string txType = "";

            switch (neoTx.Type)
            {
            case TransactionType.ClaimTransaction:
                var ctx = neoTx as ClaimTransaction;
                txType = nameof(ClaimTransaction);
                metadata.Add("claims", new JArray(ctx.Claims.Select(p => p.ToJson())));
                break;

            case TransactionType.ContractTransaction:
                txType = nameof(ContractTransaction);
                break;

            case TransactionType.InvocationTransaction:
                var itx = neoTx as InvocationTransaction;
                txType = nameof(InvocationTransaction);
                metadata.Add("script", itx.Script.ToHexString());
                metadata.Add("gas", itx.Gas.ToString());
                break;

            case TransactionType.IssueTransaction:
                txType = nameof(IssueTransaction);
                break;

            case TransactionType.MinerTransaction:
                var mtx = neoTx as MinerTransaction;
                txType = nameof(MinerTransaction);
                metadata.Add("nonce", mtx.Nonce.ToString());
                break;

            case TransactionType.StateTransaction:
                var stx = neoTx as StateTransaction;
                txType = nameof(StateTransaction);
                metadata.Add("descriptors", new JArray(stx.Descriptors.Select(p => p.ToJson())));
                break;

            default:
                return(null);
            }
            metadata.Add("tx_type", txType);

            // handle utxo transfer
            Operation[] operations  = HandleUtxoTransfer(neoTx);
            Transaction transaction = new Transaction(new TransactionIdentifier(neoTx.Hash.ToString()), operations, new Metadata(metadata));

            return(transaction);
        }
Exemple #2
0
        private Operation[] HandleUtxoTransfer(NeoTransaction tx)
        {
            var type   = OperationType.Transfer;
            var status = OperationStatus.OPERATION_STATUS_SUCCESS.Status;

            // from operations
            Operation[] fromOperations = new Operation[] { };
            for (int i = 0; i < tx.Inputs.Length; i++)
            {
                var input  = tx.Inputs[i];
                var prevTx = Blockchain.Singleton.GetTransaction(input.PrevHash);
                if (prevTx == null)
                {
                    throw new Exception("previous tx not found");
                }
                var prevOutput = prevTx.Outputs[input.PrevIndex];
                if (prevOutput == null)
                {
                    throw new Exception("previous tx output not found");
                }
                Operation operation = new Operation(new OperationIdentifier(i), type, status, null,
                                                    new AccountIdentifier(prevOutput.ScriptHash.ToAddress()),
                                                    new Amount("-" + prevOutput.Value.ToString(), new Currency(prevOutput.AssetId)),
                                                    new CoinChange(new CoinIdentifier(input.PrevHash, input.PrevIndex), CoinAction.CoinSpent)
                                                    );
                fromOperations = fromOperations.Append(operation).ToArray();
            }

            // to operations
            Operation[] toOperations = new Operation[] { };
            var         fromCount    = fromOperations.Length;

            OperationIdentifier[] relatedOperations = fromCount == 0 ? null : Enumerable.Range(0, fromCount).Select(p => new OperationIdentifier(p)).ToArray();
            //foreach (var output in tx.Outputs)
            for (int i = 0; i < tx.Outputs.Length; i++)
            {
                var       output    = tx.Outputs[i];
                Operation operation = new Operation(new OperationIdentifier(i + fromCount), type, status, relatedOperations,
                                                    new AccountIdentifier(output.ScriptHash.ToAddress()),
                                                    new Amount(output.Value.ToString(), new Currency(output.AssetId)),
                                                    new CoinChange(new CoinIdentifier(tx.Hash, i), CoinAction.CoinCreated)
                                                    );
                toOperations = toOperations.Append(operation).ToArray();
            }

            return(fromOperations.Concat(toOperations).ToArray());
        }
Exemple #3
0
        /// <summary>
        /// Hash returns the network-specific transaction hash for a signed transaction.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public JObject ConstructionHash(ConstructionHashRequest request)
        {
            NeoTransaction neoTx;

            try
            {
                neoTx = NeoTransaction.DeserializeFrom(request.SignedTransaction.HexToBytes());
            }
            catch (Exception)
            {
                return(Error.TX_DESERIALIZE_ERROR.ToJson());
            }
            var hash = neoTx.Hash.ToString();
            ConstructionHashResponse response = new ConstructionHashResponse(hash);

            return(response.ToJson());
        }
Exemple #4
0
        /// <summary>
        /// Submit a pre-signed transaction to the node. This call should not block on the transaction being included in a block.
        /// Rather, it should return immediately with an indication of whether or not the transaction was included in the mempool.
        /// The transaction submission response should only return a 200 status if the submitted transaction could be included in the mempool.
        /// Otherwise, it should return an error.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public JObject ConstructionSubmit(ConstructionSubmitRequest request)
        {
            NeoTransaction neoTx;

            try
            {
                neoTx = NeoTransaction.DeserializeFrom(request.SignedTransaction.HexToBytes());
            }
            catch (Exception)
            {
                return(Error.TX_DESERIALIZE_ERROR.ToJson());
            }

            RelayResultReason reason = system.Blockchain.Ask <RelayResultReason>(neoTx).Result;

            switch (reason)
            {
            case RelayResultReason.Succeed:
                TransactionIdentifier      transactionIdentifier = new TransactionIdentifier(neoTx.Hash.ToString());
                ConstructionSubmitResponse response = new ConstructionSubmitResponse(transactionIdentifier);
                return(response.ToJson());

            case RelayResultReason.AlreadyExists:
                return(Error.ALREADY_EXISTS.ToJson());

            case RelayResultReason.OutOfMemory:
                return(Error.OUT_OF_MEMORY.ToJson());

            case RelayResultReason.UnableToVerify:
                return(Error.UNABLE_TO_VERIFY.ToJson());

            case RelayResultReason.Invalid:
                return(Error.TX_INVALID.ToJson());

            case RelayResultReason.PolicyFail:
                return(Error.POLICY_FAIL.ToJson());

            default:
                return(Error.UNKNOWN_ERROR.ToJson());
            }
        }
Exemple #5
0
        /// <summary>
        /// Parse is called on both unsigned and signed transactions to understand the intent of the formulated transaction.
        /// This is run as a sanity check before signing (after `/construction/payloads`) and before broadcast (after `/construction/combine`).
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public JObject ConstructionParse(ConstructionParseRequest request)
        {
            NeoTransaction neoTx;

            try
            {
                if (request.Signed)
                {
                    neoTx = NeoTransaction.DeserializeFrom(request.Transaction.HexToBytes());
                }
                else
                {
                    byte[]          rawTx = request.Transaction.HexToBytes();
                    TransactionType type  = (TransactionType)rawTx[0];
                    switch (type)
                    {
                    case TransactionType.ClaimTransaction:
                        neoTx = new ClaimTransaction();
                        break;

                    case TransactionType.ContractTransaction:
                        neoTx = new ContractTransaction();
                        break;

                    case TransactionType.StateTransaction:
                        neoTx = new StateTransaction();
                        break;

                    case TransactionType.InvocationTransaction:
                        neoTx = new InvocationTransaction();
                        break;

                    default:
                        throw new ArgumentException();
                    }
                    using (MemoryStream ms = new MemoryStream(rawTx, false))
                        using (BinaryReader br = new BinaryReader(ms, Encoding.UTF8))
                        {
                            (neoTx as IVerifiable).DeserializeUnsigned(br);
                        }
                }
            }
            catch (Exception)
            {
                return(Error.TX_DESERIALIZE_ERROR.ToJson());
            }

            Transaction tx = ConvertTx(neoTx);

            Operation[] operations = tx.Operations;
            string[]    signers    = new string[0];
            if (request.Signed)
            {
                signers = GetSignersFromWitnesses(neoTx.Witnesses);
                if (signers is null)
                {
                    return(Error.TX_WITNESS_INVALID.ToJson());
                }
            }
            ConstructionParseResponse response = new ConstructionParseResponse(operations, signers, tx.Metadata);

            return(response.ToJson());
        }
Exemple #6
0
        /// <summary>
        /// Get any information required to construct a transaction for a specific network.
        /// Metadata returned here could be a recent hash to use, an account sequence number, or even arbitrary chain state.
        /// The request used when calling this endpoint is often created by calling `/construction/preprocess` in an offline environment.
        /// You should NEVER assume that the request sent to this endpoint will be created by the caller or populated with any custom parameters. This must occur in /construction/preprocess.
        /// It is important to clarify that this endpoint should not pre-construct any transactions for the client (this should happen in `/construction/payloads`).
        /// This endpoint is left purposely unstructured because of the wide scope of metadata that could be required.
        /// </summary>
        /// <returns></returns>
        public JObject ConstructionMetadata(ConstructionMetadataRequest request)
        {
            if (request.NetworkIdentifier?.Blockchain?.ToLower() != "neo n3")
            {
                return(Error.NETWORK_IDENTIFIER_INVALID.ToJson());
            }
            if (request.NetworkIdentifier?.Network?.ToLower() != network)
            {
                return(Error.NETWORK_IDENTIFIER_INVALID.ToJson());
            }

            // get signers
            Signer[] signers = Array.Empty <Signer>();
            try
            {
                signers = (request.Options["signers"] as JArray).Select(p => p.ToSigner(system.Settings.AddressVersion)).ToArray();
            }
            catch (Exception)
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }

            // get public keys, need public keys to calculate network fee
            if (!request.Options.ContainsKey("signer_metadata"))
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }
            SignerMetadata[] signerMetadatas = (request.Options["signer_metadata"] as JArray).Select(p => SignerMetadata.FromJson(p, system.Settings.AddressVersion)).ToArray();
            if (request.PublicKeys == null)
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }

            // get script
            byte[] script = Array.Empty <byte>();
            if (request.Options.ContainsKey("has_existing_script") && request.Options["has_existing_script"].AsBoolean() == true)
            {
                if (request.Options.ContainsKey("script"))
                {
                    script = Convert.FromBase64String(request.Options["script"].AsString());
                }
                else
                {
                    return(Error.PARAMETER_INVALID.ToJson());
                }
            }
            else if (request.Options.ContainsKey("has_existing_script") && request.Options["has_existing_script"].AsBoolean() == false)
            {
                if (request.Options.ContainsKey("operations_script"))
                {
                    script = Convert.FromBase64String(request.Options["operations_script"].AsString());
                }
                else
                {
                    return(Error.PARAMETER_INVALID.ToJson());
                }
            }
            else
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }

            // get max fee
            long maxFee = 2000000000;

            if (request.Options.ContainsKey("max_fee"))
            {
                maxFee = long.Parse(request.Options["max_fee"].AsString());
            }

            // invoke script
            NeoTransaction tx = new NeoTransaction
            {
                Signers    = signers,
                Attributes = Array.Empty <TransactionAttribute>(),
            };

            using ApplicationEngine engine = ApplicationEngine.Run(script, system.StoreView, container: tx, settings: system.Settings, gas: maxFee);
            if (engine.State == VMState.FAULT)
            {
                return(Error.VM_FAULT.ToJson());
            }

            // script
            tx.Script = script;

            // version
            var version = (byte)0;

            tx.Version = version;

            // nonce
            Random random = new();
            var    nonce  = (uint)random.Next();

            tx.Nonce = nonce;

            // system fee
            var sysFee = engine.GasConsumed;

            tx.SystemFee = sysFee;

            // network fee is not available here, will be calculated in
            var netFee = CalculateNetworkFee(system.StoreView, tx, request.PublicKeys, signerMetadatas);

            // valid until block
            var validUntilBlock = NativeContract.Ledger.CurrentIndex(system.StoreView) + system.Settings.MaxValidUntilBlockIncrement;

            Metadata metadata = new(new()
            {
                { "version", version },
                { "nonce", nonce },