构造交易
RpcClient
封装了交易构造模块,通过该模块可以使用特定的参数和方法构造 Neo N3 中的交易,完成个性化的功能,本篇主要介绍这部分的使用方法。
note
如果使用SDK构造要添加签名的交易,需要确保构造的RpcClient对象和其连接的区块链网络有相同的配置,否则 SDK 构造的交易在区块链中将无法验签通过,具体做法是:构造RpcClient对象时Load Neo-CLI 节点的 config.json,例如: RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json")) 。
#
交易构造步骤构造交易脚本,决定交易要执行什么样的功能,比如转账交易:
// construct the script, in this example, we will transfer 1 NEO to the receiverUInt160 scriptHash = NativeContract.NEO.Hash;byte[] script = scriptHash.MakeScript("transfer", sender, receiver, 1, "data");
构造
TransactionManagerFactory
,将RpcClient
作为参数; 构造TransactionManager
, 将Script
和Signers
作为参数。TransactionManager txManager = await new TransactionManagerFactory(client) .MakeTransactionAsync(script, signers).ConfigureAwait(false);
添加签名(单签或者多签),将账户的
KeyPair
作为签名的参数;并且签名。- 单签
// add signature for the transaction with sendKeytxManager.AddSignature(sendKey);
- 多签
// add multi-signatures for the transaction with sendKeytxManager.AddMultiSig(key1, 2, receiverKey.PublicKey, key2.PublicKey, key3.PublicKey);txManager.AddMultiSig(key2, 2, receiverKey.PublicKey, key2.PublicKey, key3.PublicKey);
多签合约
多签的本质来源于多签合约,需要先构建多签合约才能获取多签地址,进行多签转账。下面的示例使用了3个账户构成多签,验签时需要至少2个账户签名
// create a multi-signature contract, which needs at least 2 of 3 KeyPairs to signContract multiContract = Contract.CreateMultiSigContract(2, sendKey.PublicKey, key2.PublicKey, key3.PublicKey);// get the scripthash of the multi-signature contractUInt160 multiAccount = multiContract.Script.ToScriptHash();
校验签名,并将
Witness
添加至交易体。如果签名数量不够或手续费不够会引发异常。
// sign the transaction with the added signaturesTransaction tx = await txManager.SignAsync().ConfigureAwait(false);
#
交易构造示例#
构造 NEP17 转账交易下面的示例实现了从sender账户转账1024个NEO到receiver账户的功能。构建不同交易时需要关注交易中脚本和所需签名的不同。
using Neo;using Neo.Network.P2P.Payloads;using Neo.Network.RPC;using Neo.SmartContract;using Neo.SmartContract.Native;using Neo.VM;using Neo.Wallets;using System;
namespace ConsoleApp1{ class Program { static void Main(string[] args) { TestNep17Transfer().GetAwaiter().GetResult(); Console.Read(); }
private static async Task TestNep17Transfer() { // choose a neo node with rpc opened RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json")); // get the KeyPair of your account, which will pay the system and network fee KeyPair sendKey = Utility.GetKeyPair("L53tg72Az8QhYUAyyqTQ3LaXMXBE3S9mJGGZVKHBryZxya7prwhZ"); UInt160 sender = Contract.CreateSignatureContract(sendKey.PublicKey).ScriptHash;
// add Signers, which is a collection of scripthashs that need to be signed Signer[] signers = new[] { new Signer { Scopes = WitnessScope.CalledByEntry, Account = sender } };
// get the scripthash of the account you want to transfer to UInt160 receiver = Utility.GetScriptHash("NirHUAteaMr6CqWuAAMaEUScPcS3FDKebM", ProtocolSettings.Default);
// construct the script, in this example, we will transfer 1024 NEO to receiver UInt160 scriptHash = NativeContract.NEO.Hash; byte[] script = scriptHash.MakeScript("transfer", sender, receiver, 1024, "data");
// initialize the TransactionManagerFactory with rpc client and magic // fill in the TransactionManager with the script and cosigners TransactionManager txManager = await new TransactionManagerFactory(client) .MakeTransactionAsync(script, signers).ConfigureAwait(false); // add signature and sign transaction with the added signature Transaction tx = await txManager.AddSignature(sendKey).SignAsync().ConfigureAwait(false);
// broadcasts the transaction over the Neo network. await client.SendRawTransactionAsync(tx).ConfigureAwait(false); Console.WriteLine($"Transaction {tx.Hash.ToString()} is broadcasted!");
// print a message after the transaction is on chain WalletAPI neoAPI = new WalletAPI(client); await neoAPI.WaitTransactionAsync(tx) .ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}")); } }}
WalletAPI
封装了上面的过程,NEP17 转账可以简化为:
using Neo;using Neo.Network.P2P.Payloads;using Neo.Network.RPC;using Neo.SmartContract;using Neo.SmartContract.Native;using Neo.VM;using Neo.Wallets;using System;
namespace ConsoleApp1{ class Program { static void Main(string[] args) { TestNep17Transfer().GetAwaiter().GetResult(); Console.Read(); }
private static async Task TestNep17Transfer() { // choose a neo node with rpc opened RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json")); // get the KeyPair of your account, which will pay the system and network fee KeyPair sendKey = Utility.GetKeyPair("L53tg72Az8QhYUAyyqTQ3LaXMXBE3S9mJGGZVKHBryZxya7prwhZ");
// get the scripthash of the account you want to transfer to UInt160 receiver = Utility.GetScriptHash("NirHUAteaMr6CqWuAAMaEUScPcS3FDKebM", ProtocolSettings.Default);
// use WalletAPI to create and send the transfer transaction WalletAPI walletAPI = new WalletAPI(client); Transaction tx = await walletAPI.TransferAsync(NativeContract.NEO.Hash, sendKey, receiver, 1024).ConfigureAwait(false);
// print a message after the transaction is on chain WalletAPI neoAPI = new WalletAPI(client); await neoAPI.WaitTransactionAsync(tx) .ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}")); } }}
#
构造交易向多签账户转账下面的示例实现了向多签账户转账 10 个 GAS 的功能。多签账户的 scripthash 由多签合约脚本的 hash 得来。因为发送方为普通账户,添加签名的过程与上一个示例没有区别。
using Neo;using Neo.Network.P2P.Payloads;using Neo.Network.RPC;using Neo.SmartContract;using Neo.SmartContract.Native;using Neo.VM;using Neo.Wallets;using System;using Utility = Neo.Network.RPC.Utility;
namespace ConsoleApp1{ class Program { static void Main(string[] args) { TestToMultiTransfer().GetAwaiter().GetResult(); Console.Read(); }
private static async Task TestToMultiTransfer() { // choose a neo node with rpc opened RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json")); // get the KeyPair of your account, which will pay the system and network fee KeyPair sendKey = Utility.GetKeyPair("L53tg72Az8QhYUAyyqTQ3LaXMXBE3S9mJGGZVKHBryZxya7prwhZ"); UInt160 sender = Contract.CreateSignatureContract(sendKey.PublicKey).ScriptHash;
// get the KeyPair of your accounts KeyPair key2 = Utility.GetKeyPair("L1bQBbZWnKbPkpHM3jXWD3E5NwK7nui2eWHYXVZPy3t8jSFF1Qj3"); KeyPair key3 = Utility.GetKeyPair("KwrJfYyc7KWfZG5h97SYfcCQyW4jRw1njmHo48kZhZmuQWeTtUHM");
// create multi-signatures contract, this contract needs at least 2 of 3 KeyPairs to sign Contract multiContract = Contract.CreateMultiSigContract(2, new ECPoint[] { sendKey.PublicKey, key2.PublicKey, key3.PublicKey }); // get the scripthash of the multi-signature Contract UInt160 multiAccount = multiContract.Script.ToScriptHash();
// construct the script, in this example, we will transfer 1024 GAS to multi-sign account // in contract parameter, the amount type is BigInteger, so we need to muliply the contract factor UInt160 scriptHash = NativeContract.GAS.Hash; byte[] script = scriptHash.MakeScript("transfer", sender, multiAccount, 1024 * NativeContract.GAS.Factor, "data");
// add Signers, which is a collection of scripthashs that need to be signed Signer[] signers = new[] { new Signer { Scopes = WitnessScope.CalledByEntry, Account = sender } };
// initialize the TransactionManager with rpc client and magic // fill the script and signers TransactionManager txManager = await new TransactionManagerFactory(client) .MakeTransactionAsync(script, signers).ConfigureAwait(false); // add signature and sign transaction with the added signature Transaction tx = await txManager.AddSignature(sendKey).SignAsync().ConfigureAwait(false);
// broadcasts the transaction over the Neo network. await client.SendRawTransactionAsync(tx).ConfigureAwait(false); Console.WriteLine($"Transaction {tx.Hash.ToString()} is broadcasted!");
// print a message after the transaction is on chain WalletAPI neoAPI = new WalletAPI(client); await neoAPI.WaitTransactionAsync(tx) .ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}")); } }}
#
构造交易从多签账户转账下面的示例实现了从多签账户转出1024个GAS的功能。多签账户的scripthash由多签合约脚本的hash得来。因为需要从多签账户转账,添加签名时要根据多签合约要求的签名数量添加。
using Neo;using Neo.Network.P2P.Payloads;using Neo.Network.RPC;using Neo.SmartContract;using Neo.SmartContract.Native;using Neo.VM;using Neo.Wallets;using System;using Utility = Neo.Network.RPC.Utility;
namespace ConsoleApp1{ class Program { static void Main(string[] args) { TestFromMultiTransfer().GetAwaiter().GetResult(); Console.Read(); }
private static async Task TestFromMultiTransfer() { // choose a neo node with rpc opened RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));
// get the KeyPair of your account KeyPair receiverKey = Utility.GetKeyPair("L53tg72Az8QhYUAyyqTQ3LaXMXBE3S9mJGGZVKHBryZxya7prwhZ"); KeyPair key2 = Utility.GetKeyPair("L1bQBbZWnKbPkpHM3jXWD3E5NwK7nui2eWHYXVZPy3t8jSFF1Qj3"); KeyPair key3 = Utility.GetKeyPair("KwrJfYyc7KWfZG5h97SYfcCQyW4jRw1njmHo48kZhZmuQWeTtUHM");
// create multi-signature contract, this contract needs at least 2 of 3 KeyPairs to sign Contract multiContract = Contract.CreateMultiSigContract(2, new ECPoint[] { receiverKey.PublicKey, key2.PublicKey, key3.PublicKey }); // get the scripthash of the multi-signature Contract UInt160 multiAccount = multiContract.Script.ToScriptHash();
UInt160 receiver = Contract.CreateSignatureContract(receiverKey.PublicKey).ScriptHash;
// construct the script, in this example, we will transfer 1024 GAS to multi-sign account // in contract parameter, the amount type is BigInteger, so we need to muliply the contract factor UInt160 scriptHash = NativeContract.GAS.Hash; byte[] script = scriptHash.MakeScript("transfer", multiAccount, receiver, 1024 * NativeContract.GAS.Factor, "data");
// add Signers, which is a collection of scripthashs that need to be signed Signer[] signers = new[] { new Signer { Scopes = WitnessScope.CalledByEntry, Account = multiAccount } };
// initialize the TransactionManager with rpc client and magic // fill the script and signers TransactionManager txManager = await new TransactionManagerFactory(client) .MakeTransactionAsync(script, signers).ConfigureAwait(false); // add signature and sign transaction with the added signature Transaction tx = await txManager.AddMultiSig(new KeyPair[]{receiverKey, key2}, 2, receiverKey.PublicKey, key2.PublicKey, key3.PublicKey) .SignAsync().ConfigureAwait(false);
// broadcasts the transaction over the Neo network. await client.SendRawTransactionAsync(tx).ConfigureAwait(false); Console.WriteLine($"Transaction {tx.Hash.ToString()} is broadcasted!");
// print a message after the transaction is on chain WalletAPI neoAPI = new WalletAPI(client); await neoAPI.WaitTransactionAsync(tx) .ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}")); } }}