예제 #1
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.
        /// It is important to clarify that this endpoint should not pre-construct any transactions for the client (this should happen in `/construction/payloads`).
        /// </summary>
        /// <returns></returns>
        public JObject ConstructionMetadata(ConstructionMetadataRequest request)
        {
            // check params
            if (request.Options is null || request.Options.Pairs is null ||
                !request.Options.ContainsKey("tx_type"))
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }
            if (!request.Options.TryGetValue("tx_type", out JObject txType))
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }

            Metadata metadata;

            try
            {
                switch (txType.AsString())
                {
                case nameof(ClaimTransaction):
                {
                    JObject json = new JObject();
                    json["type"]      = "CoinReference[]";
                    json["required"]  = "true";
                    json["reference"] = @"https://github.com/neo-project/neo/blob/master-2.x/neo/Network/P2P/Payloads/CoinReference.cs";
                    json["example"]   = "[{\"txid\": \"0xe1e44f41a1f0854063ccdc9beb7537fc40565575e0ae2366b4a93a73c18b6166\", \"vout\": 2},{\"txid\": \"0x69fd452c92fb0e5861b27588549a8e55d3f9fee542884ae317600508bbacedbb\",\"vout\": 2}]";
                    metadata          = new Metadata(new Dictionary <string, JObject>
                        {
                            { "tx_type", nameof(ClaimTransaction) },
                            { "claims", json }
                        });
                    break;
                }

                case nameof(ContractTransaction):
                {
                    metadata = new Metadata(new Dictionary <string, JObject>
                        {
                            { "tx_type", nameof(ContractTransaction) }
                        });
                    break;
                }

                case nameof(InvocationTransaction):
                {
                    JObject script = new JObject();
                    script["type"]     = "byte[] (represented in hex string)";
                    script["required"] = "true";
                    script["example"]  = "00046e616d656724058e5e1b6008847cd662728549088a9ee82191";
                    JObject gas = new JObject();
                    gas["type"]      = "Fixed8";
                    gas["required"]  = "false";
                    gas["reference"] = @"https://github.com/neo-project/neo/blob/master-2.x/neo/Fixed8.cs";
                    gas["example"]   = "1234567890";
                    metadata         = new Metadata(new Dictionary <string, JObject>
                        {
                            { "tx_type", nameof(InvocationTransaction) },
                            { "script", script },
                            { "gas", gas }
                        });
                    break;
                }

                case nameof(StateTransaction):
                {
                    JObject json = new JObject();
                    json["type"]      = "StateDescriptor[]";
                    json["required"]  = "true";
                    json["reference"] = @"https://docs.neo.org/tutorial/zh-cn/3-transactions/3-NEO_transaction_types.html#statetransaction";
                    json["example"]   = "[{\"type\":\"Account\",\"key\":\"ce47912d51b7ed8a24a77f97f2cb6ef9a0e0bf0d\",\"field\":\"Votes\",\"value\":\"04024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba55402df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093\"}]";
                    metadata          = new Metadata(new Dictionary <string, JObject>
                        {
                            { "tx_type", nameof(StateTransaction) },
                            { "descriptors", json }
                        });
                    break;
                }

                default:
                    throw new NotSupportedException();
                }
            }
            catch (Exception)
            {
                return(Error.PARAMETER_INVALID.ToJson());
            }

            ConstructionMetadataResponse response = new ConstructionMetadataResponse(metadata);

            return(response.ToJson());
        }
예제 #2
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 },