From 75f1c2d6f204bb68b1b22a2d4aedbbfbefb2f052 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 29 Jul 2020 19:23:43 +0300 Subject: [PATCH 1/5] core: add scopes check to cosigners in DecodeBinary --- pkg/core/transaction/cosigner.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/core/transaction/cosigner.go b/pkg/core/transaction/cosigner.go index 180f43a96e..4a3b8f47d5 100644 --- a/pkg/core/transaction/cosigner.go +++ b/pkg/core/transaction/cosigner.go @@ -1,6 +1,8 @@ package transaction import ( + "errors" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" @@ -33,6 +35,14 @@ func (c *Cosigner) EncodeBinary(bw *io.BinWriter) { func (c *Cosigner) DecodeBinary(br *io.BinReader) { br.ReadBytes(c.Account[:]) c.Scopes = WitnessScope(br.ReadB()) + if c.Scopes & ^(Global|CalledByEntry|CustomContracts|CustomGroups) != 0 { + br.Err = errors.New("unknown witness scope") + return + } + if c.Scopes&Global != 0 && c.Scopes != Global { + br.Err = errors.New("global scope can not be combined with other scopes") + return + } if c.Scopes&CustomContracts != 0 { br.ReadArray(&c.AllowedContracts, maxSubitems) } From 8697582b23a39fd405324248a5484c9e778dc480 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 29 Jul 2020 19:41:15 +0300 Subject: [PATCH 2/5] core: add FeeOnly witness scope --- cli/smartcontract/smart_contract.go | 4 +++- pkg/core/transaction/transaction.go | 4 ++++ pkg/core/transaction/witness_scope.go | 13 ++++++++----- pkg/core/transaction/witness_scope_string.go | 10 +++++++--- pkg/rpc/request/param_test.go | 2 +- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 48560024c2..877a90da63 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -690,7 +690,9 @@ func parseContractConfig(confFile string) (ProjectConfig, error) { func parseCosigner(c string) (transaction.Cosigner, error) { var ( err error - res = transaction.Cosigner{} + res = transaction.Cosigner{ + Scopes: transaction.Global, + } ) data := strings.SplitN(c, ":", 2) s := data[0] diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index c5e10f249e..8716ea247d 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -152,6 +152,10 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) { br.ReadArray(&t.Cosigners, MaxCosigners) for i := 0; i < len(t.Cosigners); i++ { + if t.Cosigners[i].Scopes == FeeOnly { + br.Err = errors.New("FeeOnly scope can be used only for sender") + return + } for j := i + 1; j < len(t.Cosigners); j++ { if t.Cosigners[i].Account.Equals(t.Cosigners[j].Account) { br.Err = errors.New("transaction cosigners should be unique") diff --git a/pkg/core/transaction/witness_scope.go b/pkg/core/transaction/witness_scope.go index f5c44198e7..7d520aef54 100644 --- a/pkg/core/transaction/witness_scope.go +++ b/pkg/core/transaction/witness_scope.go @@ -11,9 +11,8 @@ import ( type WitnessScope byte const ( - // Global allows this witness in all contexts (default Neo2 behavior). - // This cannot be combined with other flags. - Global WitnessScope = 0x00 + // FeeOnly is only valid for a sender, it can't be used during the execution. + FeeOnly WitnessScope = 0 // CalledByEntry means that this condition must hold: EntryScriptHash == CallingScriptHash. // No params is needed, as the witness/permission/signature given on first invocation will // automatically expire if entering deeper internal invokes. This can be default safe @@ -23,6 +22,9 @@ const ( CustomContracts WitnessScope = 0x10 // CustomGroups define custom pubkey for group members. CustomGroups WitnessScope = 0x20 + // Global allows this witness in all contexts (default Neo2 behavior). + // This cannot be combined with other flags. + Global WitnessScope = 0x80 ) // ScopesFromString converts string of comma-separated scopes to a set of scopes @@ -40,6 +42,7 @@ func ScopesFromString(s string) (WitnessScope, error) { CalledByEntry.String(): CalledByEntry, CustomContracts.String(): CustomContracts, CustomGroups.String(): CustomGroups, + FeeOnly.String(): FeeOnly, } var isGlobal bool for _, scopeStr := range scopes { @@ -61,8 +64,8 @@ func ScopesFromString(s string) (WitnessScope, error) { // scopesToString converts witness scope to it's string representation. It uses // `, ` to separate scope names. func scopesToString(scopes WitnessScope) string { - if scopes == Global { - return "Global" + if scopes&Global != 0 || scopes == FeeOnly { + return scopes.String() } var res string if scopes&CalledByEntry != 0 { diff --git a/pkg/core/transaction/witness_scope_string.go b/pkg/core/transaction/witness_scope_string.go index 48018b76cd..02d08ede14 100644 --- a/pkg/core/transaction/witness_scope_string.go +++ b/pkg/core/transaction/witness_scope_string.go @@ -8,20 +8,22 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[Global-0] + _ = x[FeeOnly-0] _ = x[CalledByEntry-1] _ = x[CustomContracts-16] _ = x[CustomGroups-32] + _ = x[Global-128] } const ( - _WitnessScope_name_0 = "GlobalCalledByEntry" + _WitnessScope_name_0 = "FeeOnlyCalledByEntry" _WitnessScope_name_1 = "CustomContracts" _WitnessScope_name_2 = "CustomGroups" + _WitnessScope_name_3 = "Global" ) var ( - _WitnessScope_index_0 = [...]uint8{0, 6, 19} + _WitnessScope_index_0 = [...]uint8{0, 7, 20} ) func (i WitnessScope) String() string { @@ -32,6 +34,8 @@ func (i WitnessScope) String() string { return _WitnessScope_name_1 case i == 32: return _WitnessScope_name_2 + case i == 128: + return _WitnessScope_name_3 default: return "WitnessScope(" + strconv.FormatInt(int64(i), 10) + ")" } diff --git a/pkg/rpc/request/param_test.go b/pkg/rpc/request/param_test.go index 1a4f447648..e7443edc4f 100644 --- a/pkg/rpc/request/param_test.go +++ b/pkg/rpc/request/param_test.go @@ -96,7 +96,7 @@ func TestParam_UnmarshalJSON(t *testing.T) { Type: Cosigner, Value: transaction.Cosigner{ Account: accountHash, - Scopes: transaction.Global, + Scopes: transaction.FeeOnly, }, }, { From 90825efa169c6bf218dc8840882d6352b0e3974f Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 29 Jul 2020 19:57:38 +0300 Subject: [PATCH 3/5] core: move transaction's sender to cosigners Closes #1184 Ported changes from https://github.com/neo-project/neo/pull/1752 --- cli/smartcontract/smart_contract.go | 63 +++++++++-------- docs/notifications.md | 10 +-- integration/performance_test.go | 7 +- pkg/consensus/consensus_test.go | 6 +- pkg/core/blockchain.go | 14 ++-- pkg/core/blockchain_test.go | 8 +-- pkg/core/helper_test.go | 50 ++++++-------- pkg/core/interop/runtime/witness.go | 2 +- pkg/core/interop_neo_test.go | 1 + pkg/core/interop_system.go | 2 +- pkg/core/interop_system_test.go | 2 +- pkg/core/mempool/mem_pool.go | 16 ++--- pkg/core/mempool/mem_pool_test.go | 17 +++-- pkg/core/native/native_gas.go | 2 +- pkg/core/native_contract_test.go | 4 +- pkg/core/native_policy_test.go | 8 +-- .../transaction/{cosigner.go => signer.go} | 10 +-- .../{cosigner_test.go => signer_test.go} | 8 +-- pkg/core/transaction/transaction.go | 65 +++++++++--------- pkg/core/transaction/transaction_test.go | 4 ++ pkg/core/transaction/witness_scope.go | 2 +- pkg/core/util.go | 7 +- pkg/core/util_test.go | 2 +- pkg/rpc/client/nep5.go | 5 +- pkg/rpc/client/rpc.go | 60 ++++++++++------ pkg/rpc/client/rpc_test.go | 20 +++--- pkg/rpc/client/wsclient.go | 8 +-- pkg/rpc/client/wsclient_test.go | 20 +++--- pkg/rpc/request/param.go | 34 ++++----- pkg/rpc/request/param_test.go | 46 ++++++------- pkg/rpc/server/server.go | 14 ++-- pkg/rpc/server/server_test.go | 13 ++-- pkg/rpc/server/subscription.go | 16 ++--- pkg/rpc/server/subscription_test.go | 24 +++---- pkg/rpc/server/testdata/testblocks.acc | Bin 7333 -> 7236 bytes pkg/smartcontract/context/context_test.go | 1 + 36 files changed, 307 insertions(+), 264 deletions(-) rename pkg/core/transaction/{cosigner.go => signer.go} (87%) rename pkg/core/transaction/{cosigner_test.go => signer_test.go} (87%) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 877a90da63..8ebd39446f 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -148,11 +148,13 @@ func NewCommands() []cli.Command { { Name: "invokefunction", Usage: "invoke deployed contract on the blockchain", - UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...] [--] [cosigners...]", + UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...] [--] [signers...]", Description: `Executes given (as a script hash) deployed script with the given method, - arguments and cosigners. See testinvokefunction documentation for the details - about parameters. It differs from testinvokefunction in that this command - sends an invocation transaction to the network. + arguments and signers. Sender is included in the list of signers by default + with FeeOnly witness scope. If you'd like to change default sender's scope, + specify it via signers parameter. See testinvokefunction documentation for + the details about parameters. It differs from testinvokefunction in that this + command sends an invocation transaction to the network. `, Action: invokeFunction, Flags: invokeFunctionFlags, @@ -160,14 +162,15 @@ func NewCommands() []cli.Command { { Name: "testinvokefunction", Usage: "invoke deployed contract on the blockchain (test mode)", - UsageText: "neo-go contract testinvokefunction -r endpoint scripthash [method] [arguments...] [--] [cosigners...]", + UsageText: "neo-go contract testinvokefunction -r endpoint scripthash [method] [arguments...] [--] [signers...]", Description: `Executes given (as a script hash) deployed script with the given method, - arguments and cosigners. If no method is given "" is passed to the script, if - no arguments are given, an empty array is passed, if no cosigners are given, - no array will be passed. All of the given arguments are encapsulated into - array before invoking the script. The script thus should follow the regular - convention of smart contract arguments (method string and an array of other - arguments). + arguments and signers (sender is not included by default). If no method is given + "" is passed to the script, if no arguments are given, an empty array is + passed, if no signers are given no array is passed. If signers are specified, + the first one of them is treated as a sender. All of the given arguments are + encapsulated into array before invoking the script. The script thus should + follow the regular convention of smart contract arguments (method string and + an array of other arguments). Arguments always do have regular Neo smart contract parameter types, either specified explicitly or being inferred from the value. To specify the type @@ -224,12 +227,15 @@ func NewCommands() []cli.Command { * '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' - Cosigners represent a set of Uint160 hashes with witness scopes and are used - to verify hashes in System.Runtime.CheckWitness syscall. To specify cosigners - use cosigner[:scope] syntax where - * 'cosigner' is hex-encoded 160 bit (20 byte) LE value of cosigner's address, + Signers represent a set of Uint160 hashes with witness scopes and are used + to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated + as a sender. To specify signers use signer[:scope] syntax where + * 'signer' is hex-encoded 160 bit (20 byte) LE value of signer's address, which could have '0x' prefix. * 'scope' is a comma-separated set of cosigner's scopes, which could be: + - 'FeeOnly' - marks transaction's sender and can be used only for the + sender. Signer with this scope can't be used during the + script execution and only pays fees for the transaction. - 'Global' - allows this witness in all contexts. This cannot be combined with other flags. - 'CalledByEntry' - means that this condition must hold: EntryScriptHash @@ -240,8 +246,8 @@ func NewCommands() []cli.Command { - 'CustomContracts' - define valid custom contract hashes for witness check. - 'CustomGroups' - define custom pubkey for group members. - If no scopes were specified, 'Global' used as default. If no cosigners were - specified, no array will be passed. Note that scopes are properly handled by + If no scopes were specified, 'Global' used as default. If no signers were + specified, no array is passed. Note that scopes are properly handled by neo-go RPC server only. C# implementation does not support scopes capability. Examples: @@ -256,9 +262,10 @@ func NewCommands() []cli.Command { { Name: "testinvokescript", Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)", - UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [cosigners...]", + UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [signers...]", Description: `Executes given compiled AVM instructions in NEF format with the given set of - cosigners. See testinvokefunction documentation for the details about parameters. + signers not included sender by default. See testinvokefunction documentation + for the details about parameters. `, Action: testInvokeScript, Flags: testInvokeScriptFlags, @@ -389,7 +396,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { operation string params = make([]smartcontract.Parameter, 0) paramsStart = 1 - cosigners []transaction.Cosigner + cosigners []transaction.Signer cosignersStart = 0 resp *result.Invoke acc *wallet.Account @@ -493,14 +500,14 @@ func testInvokeScript(ctx *cli.Context) error { } args := ctx.Args() - var cosigners []transaction.Cosigner + var signers []transaction.Signer if args.Present() { for i, c := range args[:] { cosigner, err := parseCosigner(c) if err != nil { - return cli.NewExitError(fmt.Errorf("failed to parse cosigner #%d: %v", i+1, err), 1) + return cli.NewExitError(fmt.Errorf("failed to parse signer #%d: %v", i+1, err), 1) } - cosigners = append(cosigners, cosigner) + signers = append(signers, cosigner) } } @@ -512,7 +519,7 @@ func testInvokeScript(ctx *cli.Context) error { return err } - resp, err := c.InvokeScript(nefFile.Script, cosigners) + resp, err := c.InvokeScript(nefFile.Script, signers) if err != nil { return cli.NewExitError(err, 1) } @@ -659,7 +666,7 @@ func contractDeploy(ctx *cli.Context) error { if err != nil { return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1) } - // It doesn't require any cosigners. + // It doesn't require any signers. invRes, err := c.InvokeScript(txScript, nil) if err != nil { return cli.NewExitError(fmt.Errorf("failed to test-invoke deployment script: %v", err), 1) @@ -687,10 +694,10 @@ func parseContractConfig(confFile string) (ProjectConfig, error) { return conf, nil } -func parseCosigner(c string) (transaction.Cosigner, error) { +func parseCosigner(c string) (transaction.Signer, error) { var ( err error - res = transaction.Cosigner{ + res = transaction.Signer{ Scopes: transaction.Global, } ) @@ -706,7 +713,7 @@ func parseCosigner(c string) (transaction.Cosigner, error) { if len(data) > 1 { res.Scopes, err = transaction.ScopesFromString(data[1]) if err != nil { - return transaction.Cosigner{}, err + return transaction.Signer{}, err } } return res, nil diff --git a/docs/notifications.md b/docs/notifications.md index 9e8426ab77..99f4dd81ef 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -13,7 +13,7 @@ Currently supported events: Filters: primary ID. * new transaction in the block Contents: transaction. - Filters: sender and cosigner. + Filters: sender and signer. * notification generated during execution Contents: container hash, contract script hash, stack item. Filters: contract script hash. @@ -57,8 +57,8 @@ Recognized stream names: ConsensusData. * `transaction_added` Filter: `sender` field containing string with hex-encoded Uint160 (LE - representation) for transaction's `Sender` and/or `cosigner` in the same - format for one of transaction's `Cosigners`. + representation) for transaction's `Sender` and/or `signer` in the same + format for one of transaction's `Signers`. * `notification_from_execution` Filter: `contract` field containing string with hex-encoded Uint160 (LE representation). @@ -203,7 +203,7 @@ Example: "sysfee" : "0", "type" : "InvocationTransaction", "nonce" : 9, - "cosigners" : [ + "signers" : [ { "scopes" : 1, "account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31" @@ -262,7 +262,7 @@ Example: "nonce" : 9, "vin" : [], "type" : "InvocationTransaction", - "cosigners" : [ + "signers" : [ { "account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31", "scopes" : 1 diff --git a/integration/performance_test.go b/integration/performance_test.go index bed0b86509..dccf1748d9 100644 --- a/integration/performance_test.go +++ b/integration/performance_test.go @@ -80,7 +80,12 @@ func getTX(t *testing.B, wif *keys.WIF) *transaction.Transaction { tx := transaction.New(netmode.UnitTestNet, []byte{0x51}, 1) tx.Version = 0 - tx.Sender = fromAddressHash + tx.Signers = []transaction.Signer{ + { + Account: fromAddressHash, + Scopes: transaction.FeeOnly, + }, + } tx.Attributes = append(tx.Attributes, transaction.Attribute{ Usage: transaction.DescriptionURL, diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index b91a107f8d..4dd826e947 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -246,7 +246,11 @@ var neoOwner = testchain.MultisigScriptHash() func addSender(t *testing.T, txs ...*transaction.Transaction) { for _, tx := range txs { - tx.Sender = neoOwner + tx.Signers = []transaction.Signer{ + { + Account: neoOwner, + }, + } } } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index bd479dba13..246dc2fa14 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1226,7 +1226,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e if maxBlockSystemFee < t.SystemFee { return errors.Errorf("policy check failed: transaction's fee shouldn't exceed maximum block system fee %d", maxBlockSystemFee) } - balance := bc.GetUtilityTokenBalance(t.Sender) + balance := bc.GetUtilityTokenBalance(t.Sender()) need := t.SystemFee + t.NetworkFee if balance.Cmp(big.NewInt(need)) < 0 { return errors.Errorf("insufficient funds: balance is %v, need: %v", balance, need) @@ -1415,15 +1415,9 @@ func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) { // to verify whether the transaction is bonafide or not. // Golang implementation of GetScriptHashesForVerifying method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L190) func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([]util.Uint160, error) { - hashes := make(map[util.Uint160]bool) - hashes[t.Sender] = true - for _, c := range t.Cosigners { - hashes[c.Account] = true - } - // convert hashes to []util.Uint160 - hashesResult := make([]util.Uint160, 0, len(hashes)) - for h := range hashes { - hashesResult = append(hashesResult, h) + hashesResult := make([]util.Uint160, len(t.Signers)) + for i, s := range t.Signers { + hashesResult[i] = s.Account } return hashesResult, nil diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 480d692f33..7883ced7f0 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -107,7 +107,7 @@ func TestGetHeader(t *testing.T) { bc := newTestChain(t) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.ValidUntilBlock = bc.BlockHeight() + 1 - assert.Nil(t, addSender(tx)) + addSigners(tx) assert.Nil(t, signTx(bc, tx)) block := bc.newBlock(tx) err := bc.AddBlock(block) @@ -276,7 +276,7 @@ func TestSubscriptions(t *testing.T) { emit.Syscall(script.BinWriter, "System.Runtime.Notify") require.NoError(t, script.Err) txGood1 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0) - txGood1.Sender = neoOwner + txGood1.Signers = []transaction.Signer{{Account: neoOwner}} txGood1.Nonce = 1 txGood1.ValidUntilBlock = 100500 require.NoError(t, signTx(bc, txGood1)) @@ -288,7 +288,7 @@ func TestSubscriptions(t *testing.T) { emit.Opcode(script.BinWriter, opcode.THROW) require.NoError(t, script.Err) txBad := transaction.New(netmode.UnitTestNet, script.Bytes(), 0) - txBad.Sender = neoOwner + txBad.Signers = []transaction.Signer{{Account: neoOwner}} txBad.Nonce = 2 txBad.ValidUntilBlock = 100500 require.NoError(t, signTx(bc, txBad)) @@ -298,7 +298,7 @@ func TestSubscriptions(t *testing.T) { emit.Syscall(script.BinWriter, "System.Runtime.Notify") require.NoError(t, script.Err) txGood2 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0) - txGood2.Sender = neoOwner + txGood2.Signers = []transaction.Signer{{Account: neoOwner}} txGood2.Nonce = 3 txGood2.ValidUntilBlock = 100500 require.NoError(t, signTx(bc, txGood2)) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 912b853603..51c2a85501 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -190,8 +190,7 @@ func TestCreateBasicChain(t *testing.T) { txMoveNeo := newNEP5Transfer(neoHash, neoOwner, priv0ScriptHash, neoAmount) txMoveNeo.ValidUntilBlock = validUntilBlock txMoveNeo.Nonce = getNextNonce() - txMoveNeo.Sender = neoOwner - txMoveNeo.Cosigners = []transaction.Cosigner{{ + txMoveNeo.Signers = []transaction.Signer{{ Account: neoOwner, Scopes: transaction.CalledByEntry, AllowedContracts: nil, @@ -202,8 +201,7 @@ func TestCreateBasicChain(t *testing.T) { txMoveGas := newNEP5Transfer(gasHash, neoOwner, priv0ScriptHash, int64(util.Fixed8FromInt64(1000))) txMoveGas.ValidUntilBlock = validUntilBlock txMoveGas.Nonce = getNextNonce() - txMoveGas.Sender = neoOwner - txMoveGas.Cosigners = []transaction.Cosigner{{ + txMoveGas.Signers = []transaction.Signer{{ Account: neoOwner, Scopes: transaction.CalledByEntry, AllowedContracts: nil, @@ -212,8 +210,14 @@ func TestCreateBasicChain(t *testing.T) { require.NoError(t, signTx(bc, txMoveGas)) b := bc.newBlock(txMoveNeo, txMoveGas) require.NoError(t, bc.AddBlock(b)) - t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE()) - t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE()) + t.Logf("Block1 hash: %s", b.Hash().StringLE()) + bw := io.NewBufBinWriter() + b.EncodeBinary(bw.BinWriter) + require.NoError(t, bw.Err) + t.Logf("Block1 hex: %s", bw.Bytes()) + t.Logf("txMoveNeo hash: %s", txMoveNeo.Hash().StringLE()) + t.Logf("txMoveNeo hex: %s", hex.EncodeToString(txMoveNeo.Bytes())) + t.Logf("txMoveGas hash: %s", txMoveGas.Hash().StringLE()) require.True(t, bc.GetUtilityTokenBalance(priv0ScriptHash).Cmp(big.NewInt(1000*native.GASFactor)) >= 0) // info for getblockheader rpc tests @@ -245,12 +249,13 @@ func TestCreateBasicChain(t *testing.T) { txDeploy := transaction.New(testchain.Network(), txScript, 100*native.GASFactor) txDeploy.Nonce = getNextNonce() txDeploy.ValidUntilBlock = validUntilBlock - txDeploy.Sender = priv0ScriptHash + txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}} require.NoError(t, addNetworkFee(bc, txDeploy, acc0)) require.NoError(t, acc0.SignTx(txDeploy)) b = bc.newBlock(txDeploy) require.NoError(t, bc.AddBlock(b)) t.Logf("txDeploy: %s", txDeploy.Hash().StringLE()) + t.Logf("Block2 hash: %s", b.Hash().StringLE()) // Now invoke this contract. script = io.NewBufBinWriter() @@ -259,7 +264,7 @@ func TestCreateBasicChain(t *testing.T) { txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor) txInv.Nonce = getNextNonce() txInv.ValidUntilBlock = validUntilBlock - txInv.Sender = priv0ScriptHash + txInv.Signers = []transaction.Signer{{Account: priv0ScriptHash}} require.NoError(t, addNetworkFee(bc, txInv, acc0)) require.NoError(t, acc0.SignTx(txInv)) b = bc.newBlock(txInv) @@ -270,8 +275,7 @@ func TestCreateBasicChain(t *testing.T) { txNeo0to1 := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), 1000) txNeo0to1.Nonce = getNextNonce() txNeo0to1.ValidUntilBlock = validUntilBlock - txNeo0to1.Sender = priv0ScriptHash - txNeo0to1.Cosigners = []transaction.Cosigner{ + txNeo0to1.Signers = []transaction.Signer{ { Account: priv0ScriptHash, Scopes: transaction.CalledByEntry, @@ -290,14 +294,13 @@ func TestCreateBasicChain(t *testing.T) { initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor) initTx.Nonce = getNextNonce() initTx.ValidUntilBlock = validUntilBlock - initTx.Sender = priv0ScriptHash + initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}} require.NoError(t, addNetworkFee(bc, initTx, acc0)) require.NoError(t, acc0.SignTx(initTx)) transferTx := newNEP5Transfer(sh, sh, priv0.GetScriptHash(), 1000) transferTx.Nonce = getNextNonce() transferTx.ValidUntilBlock = validUntilBlock - transferTx.Sender = priv0ScriptHash - transferTx.Cosigners = []transaction.Cosigner{ + transferTx.Signers = []transaction.Signer{ { Account: priv0ScriptHash, Scopes: transaction.CalledByEntry, @@ -315,8 +318,7 @@ func TestCreateBasicChain(t *testing.T) { transferTx = newNEP5Transfer(sh, priv0.GetScriptHash(), priv1.GetScriptHash(), 123) transferTx.Nonce = getNextNonce() transferTx.ValidUntilBlock = validUntilBlock - transferTx.Sender = priv0ScriptHash - transferTx.Cosigners = []transaction.Cosigner{ + transferTx.Signers = []transaction.Signer{ { Account: priv0ScriptHash, Scopes: transaction.CalledByEntry, @@ -357,8 +359,7 @@ func TestCreateBasicChain(t *testing.T) { txSendRaw := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), int64(util.Fixed8FromInt64(1000))) txSendRaw.ValidUntilBlock = validUntilBlock txSendRaw.Nonce = getNextNonce() - txSendRaw.Sender = priv0ScriptHash - txSendRaw.Cosigners = []transaction.Cosigner{{ + txSendRaw.Signers = []transaction.Signer{{ Account: priv0ScriptHash, Scopes: transaction.CalledByEntry, AllowedContracts: nil, @@ -366,7 +367,7 @@ func TestCreateBasicChain(t *testing.T) { }} require.NoError(t, addNetworkFee(bc, txSendRaw, acc0)) require.NoError(t, acc0.SignTx(txSendRaw)) - bw := io.NewBufBinWriter() + bw = io.NewBufBinWriter() txSendRaw.EncodeBinary(bw.BinWriter) t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes())) } @@ -380,16 +381,9 @@ func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Trans return transaction.New(testchain.Network(), script, 10000000) } -func addSender(txs ...*transaction.Transaction) error { - for _, tx := range txs { - tx.Sender = neoOwner - } - return nil -} - -func addCosigners(txs ...*transaction.Transaction) { +func addSigners(txs ...*transaction.Transaction) { for _, tx := range txs { - tx.Cosigners = []transaction.Cosigner{{ + tx.Signers = []transaction.Signer{{ Account: neoOwner, Scopes: transaction.CalledByEntry, AllowedContracts: nil, @@ -424,7 +418,7 @@ func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.A netFee, sizeDelta := CalculateNetworkFee(sender.Contract.Script) tx.NetworkFee += netFee size += sizeDelta - for _, cosigner := range tx.Cosigners { + for _, cosigner := range tx.Signers { contract := bc.GetContractState(cosigner.Account) if contract != nil { netFee, sizeDelta = CalculateNetworkFee(contract.Script) diff --git a/pkg/core/interop/runtime/witness.go b/pkg/core/interop/runtime/witness.go index d95042ef64..948aa3a86a 100644 --- a/pkg/core/interop/runtime/witness.go +++ b/pkg/core/interop/runtime/witness.go @@ -23,7 +23,7 @@ func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) { } func checkScope(d dao.DAO, tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) { - for _, c := range tx.Cosigners { + for _, c := range tx.Signers { if c.Account == hash { if c.Scopes == transaction.Global { return true, nil diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index ed5a98f43e..5a441c3a01 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -324,6 +324,7 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Con }) tx.Attributes = attributes + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}} chain := newTestChain(t) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx) return v, tx, context, chain diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 44d9279d3d..508d02bfb3 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -167,7 +167,7 @@ func transactionToStackItem(t *transaction.Transaction) stackitem.Item { stackitem.NewByteArray(t.Hash().BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(t.Version))), stackitem.NewBigInteger(big.NewInt(int64(t.Nonce))), - stackitem.NewByteArray(t.Sender.BytesBE()), + stackitem.NewByteArray(t.Sender().BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(t.SystemFee))), stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))), stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))), diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index 57d42c6e73..58a8a5c823 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -39,7 +39,7 @@ func TestBCGetTransaction(t *testing.T) { require.Equal(t, tx.Hash().BytesBE(), actual[0].Value().([]byte)) require.Equal(t, int64(tx.Version), actual[1].Value().(*big.Int).Int64()) require.Equal(t, int64(tx.Nonce), actual[2].Value().(*big.Int).Int64()) - require.Equal(t, tx.Sender.BytesBE(), actual[3].Value().([]byte)) + require.Equal(t, tx.Sender().BytesBE(), actual[3].Value().([]byte)) require.Equal(t, int64(tx.SystemFee), actual[4].Value().(*big.Int).Int64()) require.Equal(t, int64(tx.NetworkFee), actual[5].Value().(*big.Int).Int64()) require.Equal(t, int64(tx.ValidUntilBlock), actual[6].Value().(*big.Int).Int64()) diff --git a/pkg/core/mempool/mem_pool.go b/pkg/core/mempool/mem_pool.go index c6777c5362..4a862d7dad 100644 --- a/pkg/core/mempool/mem_pool.go +++ b/pkg/core/mempool/mem_pool.go @@ -109,16 +109,16 @@ func (mp *Pool) containsKey(hash util.Uint256) bool { // tryAddSendersFee tries to add system fee and network fee to the total sender`s fee in mempool // and returns false if both balance check is required and sender has not enough GAS to pay func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer, needCheck bool) bool { - senderFee, ok := mp.fees[tx.Sender] + senderFee, ok := mp.fees[tx.Sender()] if !ok { - senderFee.balance = feer.GetUtilityTokenBalance(tx.Sender) - mp.fees[tx.Sender] = senderFee + senderFee.balance = feer.GetUtilityTokenBalance(tx.Sender()) + mp.fees[tx.Sender()] = senderFee } if needCheck && !checkBalance(tx, senderFee) { return false } senderFee.feeSum += tx.SystemFee + tx.NetworkFee - mp.fees[tx.Sender] = senderFee + mp.fees[tx.Sender()] = senderFee return true } @@ -199,9 +199,9 @@ func (mp *Pool) Remove(hash util.Uint256) { } else if num == len(mp.verifiedTxes)-1 { mp.verifiedTxes = mp.verifiedTxes[:num] } - senderFee := mp.fees[it.txn.Sender] + senderFee := mp.fees[it.txn.Sender()] senderFee.feeSum -= it.txn.SystemFee + it.txn.NetworkFee - mp.fees[it.txn.Sender] = senderFee + mp.fees[it.txn.Sender()] = senderFee } updateMempoolMetrics(len(mp.verifiedTxes)) mp.lock.Unlock() @@ -284,9 +284,9 @@ func (mp *Pool) GetVerifiedTransactions() []*transaction.Transaction { // checkTxConflicts is an internal unprotected version of Verify. func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool { - senderFee, ok := mp.fees[tx.Sender] + senderFee, ok := mp.fees[tx.Sender()] if !ok { - senderFee.balance = fee.GetUtilityTokenBalance(tx.Sender) + senderFee.balance = fee.GetUtilityTokenBalance(tx.Sender()) } return checkBalance(tx, senderFee) } diff --git a/pkg/core/mempool/mem_pool_test.go b/pkg/core/mempool/mem_pool_test.go index 2ef303ce9a..8ba90e62c4 100644 --- a/pkg/core/mempool/mem_pool_test.go +++ b/pkg/core/mempool/mem_pool_test.go @@ -31,6 +31,7 @@ func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) { mp := NewMemPool(10) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = 0 + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} _, ok := mp.TryGetValue(tx.Hash()) require.Equal(t, false, ok) require.NoError(t, mp.Add(tx, fs)) @@ -60,6 +61,7 @@ func TestOverCapacity(t *testing.T) { for i := 0; i < mempoolSize; i++ { tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = uint32(i) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} require.NoError(t, mp.Add(tx, fs)) } txcnt := uint32(mempoolSize) @@ -75,6 +77,7 @@ func TestOverCapacity(t *testing.T) { }) tx.NetworkFee = 10000 tx.Nonce = txcnt + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} txcnt++ // size is 84, networkFee is 10000 => feePerByte is 119 require.NoError(t, mp.Add(tx, fs)) @@ -89,6 +92,7 @@ func TestOverCapacity(t *testing.T) { }) tx.NetworkFee = 100 tx.Nonce = txcnt + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} txcnt++ require.Error(t, mp.Add(tx, fs)) require.Equal(t, mempoolSize, mp.Count()) @@ -98,6 +102,7 @@ func TestOverCapacity(t *testing.T) { tx = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = txcnt tx.NetworkFee = 7000 + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} txcnt++ // size is 51 (no attributes), networkFee is 7000 (<10000) // => feePerByte is 137 (>119) @@ -110,6 +115,7 @@ func TestOverCapacity(t *testing.T) { tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.NetworkFee = 8000 tx.Nonce = txcnt + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} txcnt++ require.NoError(t, mp.Add(tx, fs)) require.Equal(t, mempoolSize, mp.Count()) @@ -119,6 +125,7 @@ func TestOverCapacity(t *testing.T) { tx = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = txcnt tx.NetworkFee = 7000 + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} require.Error(t, mp.Add(tx, fs)) require.Equal(t, mempoolSize, mp.Count()) require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes))) @@ -133,6 +140,7 @@ func TestGetVerified(t *testing.T) { for i := 0; i < mempoolSize; i++ { tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = uint32(i) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} txes = append(txes, tx) require.NoError(t, mp.Add(tx, fs)) } @@ -157,6 +165,7 @@ func TestRemoveStale(t *testing.T) { for i := 0; i < mempoolSize; i++ { tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = uint32(i) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} if i%2 == 0 { txes1 = append(txes1, tx) } else { @@ -186,7 +195,7 @@ func TestMemPoolFees(t *testing.T) { sender0 := util.Uint160{1, 2, 3} tx0 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx0.NetworkFee = balance.Int64() + 1 - tx0.Sender = sender0 + tx0.Signers = []transaction.Signer{{Account: sender0}} // insufficient funds to add transaction, and balance shouldn't be stored require.Equal(t, false, mp.Verify(tx0, &FeerStub{})) require.Error(t, mp.Add(tx0, &FeerStub{})) @@ -196,7 +205,7 @@ func TestMemPoolFees(t *testing.T) { // no problems with adding another transaction with lower fee tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx1.NetworkFee = balancePart.Int64() - tx1.Sender = sender0 + tx1.Signers = []transaction.Signer{{Account: sender0}} require.NoError(t, mp.Add(tx1, &FeerStub{})) require.Equal(t, 1, len(mp.fees)) require.Equal(t, utilityBalanceAndFees{ @@ -207,7 +216,7 @@ func TestMemPoolFees(t *testing.T) { // balance shouldn't change after adding one more transaction tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx2.NetworkFee = new(big.Int).Sub(balance, balancePart).Int64() - tx2.Sender = sender0 + tx2.Signers = []transaction.Signer{{Account: sender0}} require.NoError(t, mp.Add(tx2, &FeerStub{})) require.Equal(t, 2, len(mp.verifiedTxes)) require.Equal(t, 1, len(mp.fees)) @@ -219,7 +228,7 @@ func TestMemPoolFees(t *testing.T) { // can't add more transactions as we don't have enough GAS tx3 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx3.NetworkFee = 1 - tx3.Sender = sender0 + tx3.Signers = []transaction.Signer{{Account: sender0}} require.Equal(t, false, mp.Verify(tx3, &FeerStub{})) require.Error(t, mp.Add(tx3, &FeerStub{})) require.Equal(t, 1, len(mp.fees)) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 1cb0ad5eb7..cca7655bd4 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -88,7 +88,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error { } for _, tx := range ic.Block.Transactions { absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee) - g.burn(ic, tx.Sender, absAmount) + g.burn(ic, tx.Sender(), absAmount) } validators, err := g.NEO.getNextBlockValidatorsInternal(ic.Chain, ic.DAO) if err != nil { diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index df6bad3ed1..c4d1479f3b 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -115,13 +115,13 @@ func TestNativeContract_Invoke(t *testing.T) { tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*2+10000) validUntil := chain.blockHeight + 1 tx.ValidUntilBlock = validUntil - require.NoError(t, addSender(tx)) + addSigners(tx) require.NoError(t, signTx(chain, tx)) // Enough for Call and other opcodes, but not enough for "sum" call. tx2 := transaction.New(chain.GetConfig().Magic, script, testSumPrice*2) tx2.ValidUntilBlock = chain.blockHeight + 1 - require.NoError(t, addSender(tx2)) + addSigners(tx2) require.NoError(t, signTx(chain, tx2)) b := chain.newBlock(tx, tx2) diff --git a/pkg/core/native_policy_test.go b/pkg/core/native_policy_test.go index f01c2d8917..dd3108aa05 100644 --- a/pkg/core/native_policy_test.go +++ b/pkg/core/native_policy_test.go @@ -225,12 +225,8 @@ func invokeNativePolicyMethod(chain *Blockchain, method string, args ...interfac tx := transaction.New(chain.GetConfig().Magic, script, 10000000) validUntil := chain.blockHeight + 1 tx.ValidUntilBlock = validUntil - err := addSender(tx) - if err != nil { - return nil, err - } - addCosigners(tx) - err = signTx(chain, tx) + addSigners(tx) + err := signTx(chain, tx) if err != nil { return nil, err } diff --git a/pkg/core/transaction/cosigner.go b/pkg/core/transaction/signer.go similarity index 87% rename from pkg/core/transaction/cosigner.go rename to pkg/core/transaction/signer.go index 4a3b8f47d5..8a5b54dc3a 100644 --- a/pkg/core/transaction/cosigner.go +++ b/pkg/core/transaction/signer.go @@ -11,8 +11,8 @@ import ( // The maximum number of AllowedContracts or AllowedGroups const maxSubitems = 16 -// Cosigner implements a Transaction cosigner. -type Cosigner struct { +// Signer implements a Transaction signer. +type Signer struct { Account util.Uint160 `json:"account"` Scopes WitnessScope `json:"scopes"` AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"` @@ -20,7 +20,7 @@ type Cosigner struct { } // EncodeBinary implements Serializable interface. -func (c *Cosigner) EncodeBinary(bw *io.BinWriter) { +func (c *Signer) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(c.Account[:]) bw.WriteB(byte(c.Scopes)) if c.Scopes&CustomContracts != 0 { @@ -32,10 +32,10 @@ func (c *Cosigner) EncodeBinary(bw *io.BinWriter) { } // DecodeBinary implements Serializable interface. -func (c *Cosigner) DecodeBinary(br *io.BinReader) { +func (c *Signer) DecodeBinary(br *io.BinReader) { br.ReadBytes(c.Account[:]) c.Scopes = WitnessScope(br.ReadB()) - if c.Scopes & ^(Global|CalledByEntry|CustomContracts|CustomGroups) != 0 { + if c.Scopes & ^(Global|CalledByEntry|CustomContracts|CustomGroups|FeeOnly) != 0 { br.Err = errors.New("unknown witness scope") return } diff --git a/pkg/core/transaction/cosigner_test.go b/pkg/core/transaction/signer_test.go similarity index 87% rename from pkg/core/transaction/cosigner_test.go rename to pkg/core/transaction/signer_test.go index a8789f0b9e..efe5413b0c 100644 --- a/pkg/core/transaction/cosigner_test.go +++ b/pkg/core/transaction/signer_test.go @@ -8,21 +8,21 @@ import ( ) func TestCosignerEncodeDecode(t *testing.T) { - expected := &Cosigner{ + expected := &Signer{ Account: util.Uint160{1, 2, 3, 4, 5}, Scopes: CustomContracts, AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}}, } - actual := &Cosigner{} + actual := &Signer{} testserdes.EncodeDecodeBinary(t, expected, actual) } func TestCosignerMarshallUnmarshallJSON(t *testing.T) { - expected := &Cosigner{ + expected := &Signer{ Account: util.Uint160{1, 2, 3, 4, 5}, Scopes: CustomContracts, AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}}, } - actual := &Cosigner{} + actual := &Signer{} testserdes.MarshalUnmarshalJSON(t, expected, actual) } diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 8716ea247d..948b313120 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -19,9 +19,9 @@ const ( // MaxValidUntilBlockIncrement is the upper increment size of blockhain height in blocs after // exceeding that a transaction should fail validation. It is set to be 2102400. MaxValidUntilBlockIncrement = 2102400 - // MaxCosigners is maximum number of cosigners that can be contained within a transaction. - // It is set to be 16. - MaxCosigners = 16 + // MaxAttributes is maximum number of attributes including signers that can be contained + // within a transaction. It is set to be 16. + MaxAttributes = 16 ) // Transaction is a process recorded in the NEO blockchain. @@ -32,9 +32,6 @@ type Transaction struct { // Random number to avoid hash collision. Nonce uint32 - // Address signed the transaction. - Sender util.Uint160 - // Fee to be burned. SystemFee int64 @@ -51,8 +48,8 @@ type Transaction struct { // Transaction attributes. Attributes []Attribute - // Transaction cosigners (not include Sender). - Cosigners []Cosigner + // Transaction signers list (starts with Sender). + Signers []Signer // The scripts that comes with this transaction. // Scripts exist out of the verification script @@ -96,7 +93,7 @@ func New(network netmode.Magic, script []byte, gas int64) *Transaction { Script: script, SystemFee: gas, Attributes: []Attribute{}, - Cosigners: []Cosigner{}, + Signers: []Signer{}, Scripts: []Witness{}, Network: network, } @@ -131,7 +128,6 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) { return } t.Nonce = br.ReadU32LE() - t.Sender.DecodeBinary(br) t.SystemFee = int64(br.ReadU64LE()) if t.SystemFee < 0 { br.Err = errors.New("negative system fee") @@ -148,22 +144,25 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) { } t.ValidUntilBlock = br.ReadU32LE() - br.ReadArray(&t.Attributes) - - br.ReadArray(&t.Cosigners, MaxCosigners) - for i := 0; i < len(t.Cosigners); i++ { - if t.Cosigners[i].Scopes == FeeOnly { + br.ReadArray(&t.Signers, MaxAttributes) + if len(t.Signers) == 0 { + br.Err = errors.New("signers array should contain sender") + return + } + for i := 0; i < len(t.Signers); i++ { + if i > 0 && t.Signers[i].Scopes == FeeOnly { br.Err = errors.New("FeeOnly scope can be used only for sender") return } - for j := i + 1; j < len(t.Cosigners); j++ { - if t.Cosigners[i].Account.Equals(t.Cosigners[j].Account) { - br.Err = errors.New("transaction cosigners should be unique") + for j := i + 1; j < len(t.Signers); j++ { + if t.Signers[i].Account.Equals(t.Signers[j].Account) { + br.Err = errors.New("transaction signers should be unique") return } } } + br.ReadArray(&t.Attributes, MaxAttributes-len(t.Signers)) t.Script = br.ReadVarBytes() if br.Err == nil && len(t.Script) == 0 { br.Err = errors.New("no script") @@ -201,17 +200,11 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) { } bw.WriteB(byte(t.Version)) bw.WriteU32LE(t.Nonce) - t.Sender.EncodeBinary(bw) bw.WriteU64LE(uint64(t.SystemFee)) bw.WriteU64LE(uint64(t.NetworkFee)) bw.WriteU32LE(t.ValidUntilBlock) - - // Attributes + bw.WriteArray(t.Signers) bw.WriteArray(t.Attributes) - - // Cosigners - bw.WriteArray(t.Cosigners) - bw.WriteVarBytes(t.Script) } @@ -297,6 +290,15 @@ func (t *Transaction) FeePerByte() int64 { return t.feePerByte } +// Sender returns the sender of the transaction which is always on the first place +// in the transaction's signers list. +func (t *Transaction) Sender() util.Uint160 { + if len(t.Signers) == 0 { + panic("transaction does not have signers") + } + return t.Signers[0].Account +} + // transactionJSON is a wrapper for Transaction and // used for correct marhalling of transaction.Data type transactionJSON struct { @@ -309,7 +311,7 @@ type transactionJSON struct { NetworkFee int64 `json:"netfee,string"` ValidUntilBlock uint32 `json:"validuntilblock"` Attributes []Attribute `json:"attributes"` - Cosigners []Cosigner `json:"cosigners"` + Signers []Signer `json:"signers"` Script []byte `json:"script"` Scripts []Witness `json:"witnesses"` } @@ -321,10 +323,10 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { Size: io.GetVarSize(t), Version: t.Version, Nonce: t.Nonce, - Sender: address.Uint160ToString(t.Sender), + Sender: address.Uint160ToString(t.Sender()), ValidUntilBlock: t.ValidUntilBlock, Attributes: t.Attributes, - Cosigners: t.Cosigners, + Signers: t.Signers, Script: t.Script, Scripts: t.Scripts, SystemFee: t.SystemFee, @@ -343,15 +345,10 @@ func (t *Transaction) UnmarshalJSON(data []byte) error { t.Nonce = tx.Nonce t.ValidUntilBlock = tx.ValidUntilBlock t.Attributes = tx.Attributes - t.Cosigners = tx.Cosigners + t.Signers = tx.Signers t.Scripts = tx.Scripts t.SystemFee = tx.SystemFee t.NetworkFee = tx.NetworkFee - sender, err := address.StringToUint160(tx.Sender) - if err != nil { - return errors.New("cannot unmarshal tx: bad sender") - } - t.Sender = sender t.Script = tx.Script if t.Hash() != tx.TxID { return errors.New("txid doesn't match transaction hash") diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index fe2d38a554..79d0ed7faf 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -64,6 +65,7 @@ func TestDecodeEncodeInvocationTX(t *testing.T) { func TestNew(t *testing.T) { script := []byte{0x51} tx := New(netmode.UnitTestNet, script, 1) + tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}} assert.Equal(t, int64(1), tx.SystemFee) assert.Equal(t, script, tx.Script) // Update hash fields to match tx2 that is gonna autoupdate them on decode. @@ -75,6 +77,7 @@ func TestNewTransactionFromBytes(t *testing.T) { script := []byte{0x51} tx := New(netmode.UnitTestNet, script, 1) tx.NetworkFee = 123 + tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}} data, err := testserdes.EncodeBinary(tx) require.NoError(t, err) @@ -106,6 +109,7 @@ func TestDecodingTXWithNoScript(t *testing.T) { func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) { tx := &Transaction{ Version: 0, + Signers: []Signer{{Account: util.Uint160{1, 2, 3}}}, Script: []byte{1, 2, 3, 4}, Attributes: []Attribute{}, Scripts: []Witness{}, diff --git a/pkg/core/transaction/witness_scope.go b/pkg/core/transaction/witness_scope.go index 7d520aef54..71f9a6810b 100644 --- a/pkg/core/transaction/witness_scope.go +++ b/pkg/core/transaction/witness_scope.go @@ -7,7 +7,7 @@ import ( "strings" ) -// WitnessScope represents set of witness flags for Transaction cosigner. +// WitnessScope represents set of witness flags for Transaction signer. type WitnessScope byte const ( diff --git a/pkg/core/util.go b/pkg/core/util.go index 7f1b64d836..a906de28ed 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -78,7 +78,12 @@ func deployNativeContracts(magic netmode.Magic) *transaction.Transaction { script := buf.Bytes() tx := transaction.New(magic, script, 0) tx.Nonce = 0 - tx.Sender = hash.Hash160([]byte{byte(opcode.PUSH1)}) + tx.Signers = []transaction.Signer{ + { + Account: hash.Hash160([]byte{byte(opcode.PUSH1)}), + Scopes: transaction.FeeOnly, + }, + } tx.Scripts = []transaction.Witness{ { InvocationScript: []byte{}, diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 4a42db8a70..267e04fa25 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -21,7 +21,7 @@ func TestGenesisBlockMainNet(t *testing.T) { // have been changed. Consequently, hash of the genesis block has been changed. // Update expected genesis block hash for better times. // Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf" - expect := "94e61af2441145cc251752707a58107850328a48bb095fd175ca2f8513ab5676" + expect := "ecaee33262f1bc7c7c28f2b25b54a5d61d50670871f45c0c6fe755a40cbde4a8" assert.Equal(t, expect, block.Hash().StringLE()) } diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 5f1618d9e9..0bb2afcf05 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -131,7 +131,7 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, token util.Uint1 } script := w.Bytes() - result, err := c.InvokeScript(script, []transaction.Cosigner{ + result, err := c.InvokeScript(script, []transaction.Signer{ { Account: from, Scopes: transaction.CalledByEntry, @@ -141,8 +141,7 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, token util.Uint1 return nil, fmt.Errorf("can't add system fee to transaction: %v", err) } tx := transaction.New(c.opts.Network, script, result.GasConsumed) - tx.Sender = from - tx.Cosigners = []transaction.Cosigner{ + tx.Signers = []transaction.Signer{ { Account: from, Scopes: transaction.CalledByEntry, diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index 0e1344d991..d9972d0ccd 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -345,31 +345,31 @@ func (c *Client) GetVersion() (*result.Version, error) { // InvokeScript returns the result of the given script after running it true the VM. // NOTE: This is a test invoke and will not affect the blockchain. -func (c *Client) InvokeScript(script []byte, cosigners []transaction.Cosigner) (*result.Invoke, error) { +func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) { var p = request.NewRawParams(hex.EncodeToString(script)) - return c.invokeSomething("invokescript", p, cosigners) + return c.invokeSomething("invokescript", p, signers) } // InvokeFunction returns the results after calling the smart contract scripthash // with the given operation and parameters. // NOTE: this is test invoke and will not affect the blockchain. -func (c *Client) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Cosigner) (*result.Invoke, error) { +func (c *Client) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) { var p = request.NewRawParams(contract.StringLE(), operation, params) - return c.invokeSomething("invokefunction", p, cosigners) + return c.invokeSomething("invokefunction", p, signers) } // invokeSomething is an inner wrapper for Invoke* functions -func (c *Client) invokeSomething(method string, p request.RawParams, cosigners []transaction.Cosigner) (*result.Invoke, error) { +func (c *Client) invokeSomething(method string, p request.RawParams, signers []transaction.Signer) (*result.Invoke, error) { var resp = new(result.Invoke) - if cosigners != nil { - p.Values = append(p.Values, cosigners) + if signers != nil { + p.Values = append(p.Values, signers) } if err := c.performRequest(method, p, resp); err != nil { // Retry with old-fashioned hashes (see neo/neo-modules#260). - if cosigners != nil { - var hashes = make([]util.Uint160, len(cosigners)) - for i := range cosigners { - hashes[i] = cosigners[i].Account + if signers != nil { + var hashes = make([]util.Uint160, len(signers)) + for i := range signers { + hashes[i] = signers[i].Account } p.Values[len(p.Values)-1] = hashes err = c.performRequest(method, p, resp) @@ -419,25 +419,24 @@ func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) { // SignAndPushInvocationTx signs and pushes given script as an invocation // transaction using given wif to sign it and spending the amount of gas // specified. It returns a hash of the invocation transaction and an error. -func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee int64, netfee util.Fixed8, cosigners []transaction.Cosigner) (util.Uint256, error) { +func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee int64, netfee util.Fixed8, cosigners []transaction.Signer) (util.Uint256, error) { var txHash util.Uint256 var err error tx := transaction.New(c.opts.Network, script, sysfee) tx.SystemFee = sysfee - tx.Cosigners = cosigners - validUntilBlock, err := c.CalculateValidUntilBlock() + addr, err := address.StringToUint160(acc.Address) if err != nil { - return txHash, errors.Wrap(err, "failed to add validUntilBlock to transaction") + return txHash, errors.Wrap(err, "failed to get address") } - tx.ValidUntilBlock = validUntilBlock + tx.Signers = getSigners(addr, cosigners) - addr, err := address.StringToUint160(acc.Address) + validUntilBlock, err := c.CalculateValidUntilBlock() if err != nil { - return txHash, errors.Wrap(err, "failed to get address") + return txHash, errors.Wrap(err, "failed to add validUntilBlock to transaction") } - tx.Sender = addr + tx.ValidUntilBlock = validUntilBlock err = c.AddNetworkFee(tx, int64(netfee), acc) if err != nil { @@ -458,6 +457,27 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys return txHash, nil } +// getSigners returns an array of transaction signers from given sender and cosigners. +// If cosigners list already contains sender, the sender will be placed at the start of +// the list. +func getSigners(sender util.Uint160, cosigners []transaction.Signer) []transaction.Signer { + s := transaction.Signer{ + Account: sender, + Scopes: transaction.FeeOnly, + } + for i, c := range cosigners { + if c.Account == sender { + if i == 0 { + return cosigners + } + s.Scopes = c.Scopes + cosigners = append(cosigners[:i], cosigners[i+1:]...) + break + } + } + return append([]transaction.Signer{s}, cosigners...) +} + // ValidateAddress verifies that the address is a correct NEO address. func (c *Client) ValidateAddress(address string) error { var ( @@ -513,7 +533,7 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, acc tx.NetworkFee += netFee size += sizeDelta } - for _, cosigner := range tx.Cosigners { + for _, cosigner := range tx.Signers { script := acc.Contract.Script if !cosigner.Account.Equals(hash.Hash160(acc.Contract.Script)) { contract, err := c.GetContractState(cosigner.Account) diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 07b683fd7c..9866e771b0 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -41,17 +41,17 @@ type rpcClientTestCase struct { check func(t *testing.T, c *Client, result interface{}) } -const hexB1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54442fd56401107247de4e35599d1feffaf6e763972f738d2858b0a22ad06523867e4dcf921f7c1c67201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c400f01eb6371a135527ddb205a0dae1f69d2d324837cce128bead9033091883f9ce21e6d759a40f690746592b021d397f088e3b7b417fcef73cd1bfdc2800769520c4084f7ae5d9f58a09aa56eeb2f282744a59a82d17e3be0eb1c7f54e6e8df79620e2608191bac57280a836db2ec2a776d07e43f16bc76c47d348b0fcd09d56c7c320c402b95b4e39ec35162a640795ff223e92dec95390a072eb457f6a4323052f80731517e0df029e4a457204f777f5261c6a4a88d46d4c3abdec635a6eed580d6c77f0c40b9735745b1dd795a258c31d8e6fa87d5cfc2e9b3a4890d610d33bcf833b64c58b0c5cea17f3a7128f1065ed193e636971590f193f28bdd1eeebbbc1fe6e7cee594130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb030057040000000000000002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb0003000000abec5362f11e75b6e02e407bb98d63675d1438410000000000000000de6e0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d143841015d0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b523801fd08010c408710e7b5d8ac6cd8d09d6fb35bf2dcce5f5fb38595ddb6d04a570bc925bc2c55a73cdf5f3cb5a1feb0acc4f26c8bbca6c43df3b4a98b8c3c2c809c2f096eb25a0c40c186c102cbf72313fd94df5077fc5bbefcd32227ed2159a47a46594877fa39f330d8223b45aa24aff005bebb5b2427a50c8c4de8618e7d7b00d73d836c44942e0c40a243b5b565bd4bc2f0bb112f7624f6b3514c12413c5a95230819face3f760f03fcb96b188f98038a3f251686b53a88d69744f4e4a985e6297003a80cdb169e800c404a951e61ac99d5ee31831d111754adb711b4a9060a9524fe383a90771843cdb096382674027a87f2a15a83bd77deb2b2ab1fe8b3de6e546293ef3df9b1129e2894130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" +const hexB1 = "00000000c0da501a765dcc77a8f2a87c4106cfc0219ff944dc66b87b228ceb95785796a40698a5fbc984182e90f519272f1364568f02f4c845e12d953a4f816d63840f2b19b10e9f73010000010000005e12bea87aeb955884f6f9ce78458cccb97c309501fd08010c400d006b6ec3bf952d536f9a911218be3ec6a94a8014885a630112d0018bcea2c5fb7643b01c68e0bcf4cfb229522e4de97d768549702bdfc7ddc2710eb70d9a820c406dc141248eeccff61f194cbd0610a7c6af4d696b39a1f2cd93b302607b9b001abc9813ffa48f0e4d08b166a3084f53bdea51843d8ead7330d31717e0deab1a7f0c40e4cad924c0979ce67e31dbe979cec6474fa4135c9b01c993965da18f54928af0278371c65b6bb31fe0c752916a92b7ad7152d96033622a85efc8ba891ffd91170c400e1fbd92d760ae03156ed17346ca3ddc80cb5a77e060871bebd7a4fdf842da5a4a51e24a36e85752b580b65e20a4cda9d74ed003d48779ad112cc5f15e29bf3794130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b41138defaf03005704000000000000000200000080969800000000009e7c440000000000b0040000015e12bea87aeb955884f6f9ce78458cccb97c30950100590218ddf5050c14aa8acf859d4fe402b34e673f2156821796a488eb0c145e12bea87aeb955884f6f9ce78458cccb97c309513c00c087472616e736665720c1425059ecb4878d3a875f91c51ceded330d4575fde41627d5b523801fd08010c400871252c02d46a978b7e2047a8fa9e3e608edb03a3c6fb5c715243c86f6bcd95a325e0fba68d05454fe6e0da57710986bacdc7287389f419faf28bcd84846a460c4091e1f403af89e3a3de2617afc603e387d18e64ef7e7218c1191ecf7ae7ce81469e9fd004317e620fbcb712e187aba3da74ce0565f410e0dd3c54e69891ca54790c401c8ce4fb2631303519ca8030e45df87add8737328acc6dd6f66ac4e20eed1a7b1cb93395dc8a40358f2a494f8c248718e9d2bae0159452cde621c15d0a973fb20c4088a292b12dde29fa12c7c45ab4ed67df5ddfbeb0dd162115b3bcc267702ac9a923fccf3eb9afc4c6145cef64c7467a558a3bb2a104e8482bac2bcae58d95a0c894130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b41138defaf000300000080969800000000003e8c440000000000b0040000015e12bea87aeb955884f6f9ce78458cccb97c309501005d0300e87648170000000c14aa8acf859d4fe402b34e673f2156821796a488eb0c145e12bea87aeb955884f6f9ce78458cccb97c309513c00c087472616e736665720c14bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e6641627d5b523801fd08010c408a50f97d779d365cf239eb8102d81ae8aa9376ef8b95de7d73ea40810a66d2ef5e65ce63db876e0b11232c00fd931688617619b5e9a0df36c3e2e6d901fc51380c40f3245b6cf5561cd65222bc6019652c400b3015ffde8a9f6580692ddb0c894024709286c7f3f919f736ebcccaeb6e8d8338bf2122a405fd8248dcbc558629139b0c40a423cf37a27846f5a613c704160f1b26a1fba2a284493a273f709d47f27a9412f32a01360a81d1da04e241558b0aabd33da14bff7e3e8915b303adc6acbe989a0c4084aa4ab66f9d1a11cd4d18cf43040b182c47991eb3350705f76b6b4139e783fca2ed99381ad4866882c660a62b3604e4e864ee8bb8df45258727fbf886702e6e94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b41138defaf" -const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" +const hexTxMoveNeo = "000200000080969800000000009e7c440000000000b0040000015e12bea87aeb955884f6f9ce78458cccb97c30950100590218ddf5050c14aa8acf859d4fe402b34e673f2156821796a488eb0c145e12bea87aeb955884f6f9ce78458cccb97c309513c00c087472616e736665720c1425059ecb4878d3a875f91c51ceded330d4575fde41627d5b523801fd08010c400871252c02d46a978b7e2047a8fa9e3e608edb03a3c6fb5c715243c86f6bcd95a325e0fba68d05454fe6e0da57710986bacdc7287389f419faf28bcd84846a460c4091e1f403af89e3a3de2617afc603e387d18e64ef7e7218c1191ecf7ae7ce81469e9fd004317e620fbcb712e187aba3da74ce0565f410e0dd3c54e69891ca54790c401c8ce4fb2631303519ca8030e45df87add8737328acc6dd6f66ac4e20eed1a7b1cb93395dc8a40358f2a494f8c248718e9d2bae0159452cde621c15d0a973fb20c4088a292b12dde29fa12c7c45ab4ed67df5ddfbeb0dd162115b3bcc267702ac9a923fccf3eb9afc4c6145cef64c7467a558a3bb2a104e8482bac2bcae58d95a0c894130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b41138defaf" -const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1681,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d","confirmations":6,"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensusdata":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sysfee":"0","netfee":"876350","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","witnesses":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]},{"hash":"0xb661d5e4d9e41c3059b068f8abb6f1566a47ec800879e34c0ebd2799781a2760","size":579,"version":0,"nonce":3,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sysfee":"0","netfee":"880350","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBQxboUQOQGdOd/Cw31sP+4Z/VgJhwwUq+xTYvEedbbgLkB7uY1jZ10UOEETwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4","witnesses":[{"invocation":"DECHEOe12Kxs2NCdb7Nb8tzOX1+zhZXdttBKVwvJJbwsVac83188taH+sKzE8myLvKbEPfO0qYuMPCyAnC8JbrJaDEDBhsECy/cjE/2U31B3/Fu+/NMiJ+0hWaR6RllId/o58zDYIjtFqiSv8AW+u1skJ6UMjE3oYY59ewDXPYNsRJQuDECiQ7W1Zb1LwvC7ES92JPazUUwSQTxalSMIGfrOP3YPA/y5axiPmAOKPyUWhrU6iNaXRPTkqYXmKXADqAzbFp6ADEBKlR5hrJnV7jGDHREXVK23EbSpBgqVJP44OpB3GEPNsJY4JnQCeofyoVqDvXfesrKrH+iz3m5UYpPvPfmxEp4o","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}]}}` +const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1641,"nextblockhash":"0x003abea54aa3c5edba7e33fb7ca96452cb65ff8cd36ce1cdfd412a6c4d3ea38a","confirmations":6,"hash":"0xd9518e322440714b0564d6f84a9a39b527b5480e4e7f7932895777a4c8fa0a9e","version":0,"previousblockhash":"0xa496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0","merkleroot":"0x2b0f84636d814f3a952de145c8f4028f5664132f2719f5902e1884c9fba59806","time":1596101407001,"index":1,"nextconsensus":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","witnesses":[{"invocation":"DEANAGtuw7+VLVNvmpESGL4+xqlKgBSIWmMBEtABi86ixft2Q7AcaOC89M+yKVIuTel9doVJcCvfx93CcQ63DZqCDEBtwUEkjuzP9h8ZTL0GEKfGr01pazmh8s2TswJge5sAGryYE/+kjw5NCLFmowhPU73qUYQ9jq1zMNMXF+Deqxp/DEDkytkkwJec5n4x2+l5zsZHT6QTXJsByZOWXaGPVJKK8CeDccZba7Mf4MdSkWqSt61xUtlgM2Iqhe/Iuokf/ZEXDEAOH72S12CuAxVu0XNGyj3cgMtad+Bghxvr16T9+ELaWkpR4ko26FdStYC2XiCkzanXTtAD1Id5rREsxfFeKb83","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}],"consensusdata":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0x32f9bd3a2707475407c41bf5daacf9560e25ed74f6d85b3afb2ef72edb2325ba","size":555,"version":0,"nonce":2,"sender":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","sysfee":"10000000","netfee":"4488350","validuntilblock":1200,"attributes":[],"signers":[{"account":"0x95307cb9cc8c4578cef9f6845895eb7aa8be125e","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFKqKz4WdT+QCs05nPyFWgheWpIjrDBReEr6oeuuVWIT2+c54RYzMuXwwlRPADAh0cmFuc2ZlcgwUJQWey0h406h1+RxRzt7TMNRXX95BYn1bUjg=","witnesses":[{"invocation":"DEAIcSUsAtRql4t+IEeo+p4+YI7bA6PG+1xxUkPIb2vNlaMl4PumjQVFT+bg2ldxCYa6zccoc4n0Gfryi82EhGpGDECR4fQDr4njo94mF6/GA+OH0Y5k735yGMEZHs96586BRp6f0AQxfmIPvLcS4Yero9p0zgVl9BDg3TxU5piRylR5DEAcjOT7JjEwNRnKgDDkXfh63Yc3MorMbdb2asTiDu0aexy5M5XcikA1jypJT4wkhxjp0rrgFZRSzeYhwV0Klz+yDECIopKxLd4p+hLHxFq07WffXd++sN0WIRWzvMJncCrJqSP8zz65r8TGFFzvZMdGelWKO7KhBOhIK6wryuWNlaDI","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}]},{"hash":"0xd35d6386ec2f29b90839536f6af9466098d1665e951cdd0a20db6b4629b08369","size":559,"version":0,"nonce":3,"sender":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","sysfee":"10000000","netfee":"4492350","validuntilblock":1200,"attributes":[],"signers":[{"account":"0x95307cb9cc8c4578cef9f6845895eb7aa8be125e","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBSqis+FnU/kArNOZz8hVoIXlqSI6wwUXhK+qHrrlViE9vnOeEWMzLl8MJUTwAwIdHJhbnNmZXIMFLyvQdaEx9StbuDZnalwe50fDI5mQWJ9W1I4","witnesses":[{"invocation":"DECKUPl9d502XPI564EC2BroqpN274uV3n1z6kCBCmbS715lzmPbh24LESMsAP2TFohhdhm16aDfNsPi5tkB/FE4DEDzJFts9VYc1lIivGAZZSxACzAV/96Kn2WAaS3bDIlAJHCShsfz+Rn3NuvMyutujYM4vyEipAX9gkjcvFWGKRObDECkI883onhG9aYTxwQWDxsmofuiooRJOic/cJ1H8nqUEvMqATYKgdHaBOJBVYsKq9M9oUv/fj6JFbMDrcasvpiaDECEqkq2b50aEc1NGM9DBAsYLEeZHrM1BwX3a2tBOeeD/KLtmTga1IZogsZgpis2BOToZO6LuN9FJYcn+/iGcC5u","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}]}]}}` -const hexHeader1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54442fd56401107247de4e35599d1feffaf6e763972f738d2858b0a22ad06523867e4dcf921f7c1c67201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c400f01eb6371a135527ddb205a0dae1f69d2d324837cce128bead9033091883f9ce21e6d759a40f690746592b021d397f088e3b7b417fcef73cd1bfdc2800769520c4084f7ae5d9f58a09aa56eeb2f282744a59a82d17e3be0eb1c7f54e6e8df79620e2608191bac57280a836db2ec2a776d07e43f16bc76c47d348b0fcd09d56c7c320c402b95b4e39ec35162a640795ff223e92dec95390a072eb457f6a4323052f80731517e0df029e4a457204f777f5261c6a4a88d46d4c3abdec635a6eed580d6c77f0c40b9735745b1dd795a258c31d8e6fa87d5cfc2e9b3a4890d610d33bcf833b64c58b0c5cea17f3a7128f1065ed193e636971590f193f28bdd1eeebbbc1fe6e7cee594130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00" +const hexHeader1 = "00000000c0da501a765dcc77a8f2a87c4106cfc0219ff944dc66b87b228ceb95785796a40698a5fbc984182e90f519272f1364568f02f4c845e12d953a4f816d63840f2b19b10e9f73010000010000005e12bea87aeb955884f6f9ce78458cccb97c309501fd08010c400d006b6ec3bf952d536f9a911218be3ec6a94a8014885a630112d0018bcea2c5fb7643b01c68e0bcf4cfb229522e4de97d768549702bdfc7ddc2710eb70d9a820c406dc141248eeccff61f194cbd0610a7c6af4d696b39a1f2cd93b302607b9b001abc9813ffa48f0e4d08b166a3084f53bdea51843d8ead7330d31717e0deab1a7f0c40e4cad924c0979ce67e31dbe979cec6474fa4135c9b01c993965da18f54928af0278371c65b6bb31fe0c752916a92b7ad7152d96033622a85efc8ba891ffd91170c400e1fbd92d760ae03156ed17346ca3ddc80cb5a77e060871bebd7a4fdf842da5a4a51e24a36e85752b580b65e20a4cda9d74ed003d48779ad112cc5f15e29bf3794130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b41138defaf00" -const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","size":518,"version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"confirmations":6,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d"}}` +const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0xd9518e322440714b0564d6f84a9a39b527b5480e4e7f7932895777a4c8fa0a9e","size":518,"version":0,"previousblockhash":"0xa496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0","merkleroot":"0x2b0f84636d814f3a952de145c8f4028f5664132f2719f5902e1884c9fba59806","time":1596101407001,"index":1,"nextconsensus":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","witnesses":[{"invocation":"DEANAGtuw7+VLVNvmpESGL4+xqlKgBSIWmMBEtABi86ixft2Q7AcaOC89M+yKVIuTel9doVJcCvfx93CcQ63DZqCDEBtwUEkjuzP9h8ZTL0GEKfGr01pazmh8s2TswJge5sAGryYE/+kjw5NCLFmowhPU73qUYQ9jq1zMNMXF+Deqxp/DEDkytkkwJec5n4x2+l5zsZHT6QTXJsByZOWXaGPVJKK8CeDccZba7Mf4MdSkWqSt61xUtlgM2Iqhe/Iuokf/ZEXDEAOH72S12CuAxVu0XNGyj3cgMtad+Bghxvr16T9+ELaWkpR4ko26FdStYC2XiCkzanXTtAD1Id5rREsxfFeKb83","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}],"confirmations":6,"nextblockhash":"0x003abea54aa3c5edba7e33fb7ca96452cb65ff8cd36ce1cdfd412a6c4d3ea38a"}}` -const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","confirmations":6,"blocktime":1592472500001,"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sysfee":"0","netfee":"876350","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","witnesses":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}}` +const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0xd9518e322440714b0564d6f84a9a39b527b5480e4e7f7932895777a4c8fa0a9e","confirmations":6,"blocktime":1596101407001,"vmstate":"","hash":"0x32f9bd3a2707475407c41bf5daacf9560e25ed74f6d85b3afb2ef72edb2325ba","size":555,"version":0,"nonce":2,"sender":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","sysfee":"10000000","netfee":"4488350","validuntilblock":1200,"attributes":[],"signers":[{"account":"0x95307cb9cc8c4578cef9f6845895eb7aa8be125e","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFKqKz4WdT+QCs05nPyFWgheWpIjrDBReEr6oeuuVWIT2+c54RYzMuXwwlRPADAh0cmFuc2ZlcgwUJQWey0h406h1+RxRzt7TMNRXX95BYn1bUjg=","witnesses":[{"invocation":"DEAIcSUsAtRql4t+IEeo+p4+YI7bA6PG+1xxUkPIb2vNlaMl4PumjQVFT+bg2ldxCYa6zccoc4n0Gfryi82EhGpGDECR4fQDr4njo94mF6/GA+OH0Y5k735yGMEZHs96586BRp6f0AQxfmIPvLcS4Yero9p0zgVl9BDg3TxU5piRylR5DEAcjOT7JjEwNRnKgDDkXfh63Yc3MorMbdb2asTiDu0aexy5M5XcikA1jypJT4wkhxjp0rrgFZRSzeYhwV0Klz+yDECIopKxLd4p+hLHxFq07WffXd++sN0WIRWzvMJncCrJqSP8zz65r8TGFFzvZMdGelWKO7KhBOhIK6wryuWNlaDI","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}]}}` // getResultBlock1 returns data for block number 1 which is used by several tests. func getResultBlock1() *result.Block { @@ -64,14 +64,14 @@ func getResultBlock1() *result.Block { if err != nil { panic(err) } - b2Hash, err := util.Uint256DecodeStringLE("45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d") + b2Hash, err := util.Uint256DecodeStringLE("003abea54aa3c5edba7e33fb7ca96452cb65ff8cd36ce1cdfd412a6c4d3ea38a") if err != nil { panic(err) } return &result.Block{ Block: *b, BlockMetadata: result.BlockMetadata{ - Size: 1681, + Size: 1641, NextBlockHash: &b2Hash, Confirmations: 6, }, @@ -635,7 +635,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ Type: smartcontract.Hash160Type, Value: hash, }, - }, []transaction.Cosigner{{ + }, []transaction.Signer{{ Account: util.Uint160{1, 2, 3}, }}) }, @@ -662,7 +662,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ if err != nil { panic(err) } - return c.InvokeScript(script, []transaction.Cosigner{{ + return c.InvokeScript(script, []transaction.Signer{{ Account: util.Uint160{1, 2, 3}, }}) }, diff --git a/pkg/rpc/client/wsclient.go b/pkg/rpc/client/wsclient.go index 4ce50b3a1c..b09bb57eac 100644 --- a/pkg/rpc/client/wsclient.go +++ b/pkg/rpc/client/wsclient.go @@ -255,12 +255,12 @@ func (c *WSClient) SubscribeForNewBlocks(primary *int) (string, error) { } // SubscribeForNewTransactions adds subscription for new transaction events to -// this instance of client. It can be filtered by sender and/or cosigner, nil +// this instance of client. It can be filtered by sender and/or signer, nil // value is treated as missing filter. -func (c *WSClient) SubscribeForNewTransactions(sender *util.Uint160, cosigner *util.Uint160) (string, error) { +func (c *WSClient) SubscribeForNewTransactions(sender *util.Uint160, signer *util.Uint160) (string, error) { params := request.NewRawParams("transaction_added") - if sender != nil || cosigner != nil { - params.Values = append(params.Values, request.TxFilter{Sender: sender, Cosigner: cosigner}) + if sender != nil || signer != nil { + params.Values = append(params.Values, request.TxFilter{Sender: sender, Signer: signer}) } return c.performSubscription(params) } diff --git a/pkg/rpc/client/wsclient_test.go b/pkg/rpc/client/wsclient_test.go index 79e0af19da..e76f363a7c 100644 --- a/pkg/rpc/client/wsclient_test.go +++ b/pkg/rpc/client/wsclient_test.go @@ -119,7 +119,7 @@ func TestWSClientEvents(t *testing.T) { `{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","trigger":"Application","vmstate":"HALT","gasconsumed":"22910000","stack":[],"notifications":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}},{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"transfer","state":{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}}]}]}`, `{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}}]}`, `{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","trigger":"Application","vmstate":"HALT","gasconsumed":"6042610","stack":[],"notifications":[{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","eventname":"contract call","state":{"type":"Array","value":[{"type":"ByteString","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteString","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}]}},{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","eventname":"transfer","state":{"type":"Array","value":[{"type":"ByteString","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteString","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}}]}]}`, - `{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x2d312f6379ead13cf62634c703091b750e7903728df2a3cf5bd96ce80b84a849","version":0,"previousblockhash":"0xb8237d34c156cac6be7b01578decf8ac8c99a62f0b6f774d622aad7be0fe189d","merkleroot":"0xf89169e89361692b71e671f13c088e84c5325015c413e8f89e7ba38efdb41287","time":1592472500006,"index":6,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEDblVguNGXWbUswDvBfVJzBt76BJyJ0Ga6siquyjioGn4Dbr6zy1IvcLl3xN5akcejRy9e+Mr1qvpe/gkLgtW4QDEDRwPISZagMFjE/plXTnZ/gEU0IbBAAe23U29zVWteUmzRsPxF/MdzXvdffR9W0edkj17AmkWpn+5rqzH9aCOpLDECEvjgxZaRoAHEDNzp1REllLcGzMwrwSjudtzfgRglQL3g1BKerDx6cGHH73medRVkL9QVm4KzSxlywVtvhwBMrDEBuPKvzg5TtakFW2jr/bfmy1bn2FiLARlOySwaGdKRV93ozA5lVEIAvHbBlJtT4/5H8jHjbncXXMrP3OUHqebZz","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensusdata":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","size":265,"version":0,"nonce":9,"sender":"NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN","sysfee":"0","netfee":"365210","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":"CalledByEntry"}],"script":"AHsMFCBygnSvr8NvQ6Bx0yjPo+Yp2cuwDBQxboUQOQGdOd/Cw31sP+4Z/VgJhxPADAh0cmFuc2ZlcgwU2gHvYphOfQUnXEpYeyAtoLP3X+ZBYn1bUjg=","witnesses":[{"invocation":"DECwklSj3liZOJbktRtkVdUCu8U2LQlrU6Dv8NtMgd0xXbk5lXjc2p68xv6xtJXbJ4aoFMJZ9lkcNpGoeUCcaCet","verification":"DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQatQ="}]}]}]}`, + `{"jsonrpc":"2.0","method":"block_added","params":[{"size":1641,"nextblockhash":"0x003abea54aa3c5edba7e33fb7ca96452cb65ff8cd36ce1cdfd412a6c4d3ea38a","confirmations":6,"hash":"0xd9518e322440714b0564d6f84a9a39b527b5480e4e7f7932895777a4c8fa0a9e","version":0,"previousblockhash":"0xa496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0","merkleroot":"0x2b0f84636d814f3a952de145c8f4028f5664132f2719f5902e1884c9fba59806","time":1596101407001,"index":1,"nextconsensus":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","witnesses":[{"invocation":"DEANAGtuw7+VLVNvmpESGL4+xqlKgBSIWmMBEtABi86ixft2Q7AcaOC89M+yKVIuTel9doVJcCvfx93CcQ63DZqCDEBtwUEkjuzP9h8ZTL0GEKfGr01pazmh8s2TswJge5sAGryYE/+kjw5NCLFmowhPU73qUYQ9jq1zMNMXF+Deqxp/DEDkytkkwJec5n4x2+l5zsZHT6QTXJsByZOWXaGPVJKK8CeDccZba7Mf4MdSkWqSt61xUtlgM2Iqhe/Iuokf/ZEXDEAOH72S12CuAxVu0XNGyj3cgMtad+Bghxvr16T9+ELaWkpR4ko26FdStYC2XiCkzanXTtAD1Id5rREsxfFeKb83","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}],"consensusdata":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0x32f9bd3a2707475407c41bf5daacf9560e25ed74f6d85b3afb2ef72edb2325ba","size":555,"version":0,"nonce":2,"sender":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","sysfee":"10000000","netfee":"4488350","validuntilblock":1200,"attributes":[],"signers":[{"account":"0x95307cb9cc8c4578cef9f6845895eb7aa8be125e","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFKqKz4WdT+QCs05nPyFWgheWpIjrDBReEr6oeuuVWIT2+c54RYzMuXwwlRPADAh0cmFuc2ZlcgwUJQWey0h406h1+RxRzt7TMNRXX95BYn1bUjg=","witnesses":[{"invocation":"DEAIcSUsAtRql4t+IEeo+p4+YI7bA6PG+1xxUkPIb2vNlaMl4PumjQVFT+bg2ldxCYa6zccoc4n0Gfryi82EhGpGDECR4fQDr4njo94mF6/GA+OH0Y5k735yGMEZHs96586BRp6f0AQxfmIPvLcS4Yero9p0zgVl9BDg3TxU5piRylR5DEAcjOT7JjEwNRnKgDDkXfh63Yc3MorMbdb2asTiDu0aexy5M5XcikA1jypJT4wkhxjp0rrgFZRSzeYhwV0Klz+yDECIopKxLd4p+hLHxFq07WffXd++sN0WIRWzvMJncCrJqSP8zz65r8TGFFzvZMdGelWKO7KhBOhIK6wryuWNlaDI","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}]},{"hash":"0xd35d6386ec2f29b90839536f6af9466098d1665e951cdd0a20db6b4629b08369","size":559,"version":0,"nonce":3,"sender":"NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY","sysfee":"10000000","netfee":"4492350","validuntilblock":1200,"attributes":[],"signers":[{"account":"0x95307cb9cc8c4578cef9f6845895eb7aa8be125e","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBSqis+FnU/kArNOZz8hVoIXlqSI6wwUXhK+qHrrlViE9vnOeEWMzLl8MJUTwAwIdHJhbnNmZXIMFLyvQdaEx9StbuDZnalwe50fDI5mQWJ9W1I4","witnesses":[{"invocation":"DECKUPl9d502XPI564EC2BroqpN274uV3n1z6kCBCmbS715lzmPbh24LESMsAP2TFohhdhm16aDfNsPi5tkB/FE4DEDzJFts9VYc1lIivGAZZSxACzAV/96Kn2WAaS3bDIlAJHCShsfz+Rn3NuvMyutujYM4vyEipAX9gkjcvFWGKRObDECkI883onhG9aYTxwQWDxsmofuiooRJOic/cJ1H8nqUEvMqATYKgdHaBOJBVYsKq9M9oUv/fj6JFbMDrcasvpiaDECEqkq2b50aEc1NGM9DBAsYLEeZHrM1BwX3a2tBOeeD/KLtmTga1IZogsZgpis2BOToZO6LuN9FJYcn+/iGcC5u","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw=="}]}]}]}`, `{"jsonrpc":"2.0","method":"event_missed","params":[]}`, } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { @@ -204,13 +204,13 @@ func TestWSFilteredSubscriptions(t *testing.T) { filt, ok := param.Value.(request.TxFilter) require.Equal(t, true, ok) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender) - require.Nil(t, filt.Cosigner) + require.Nil(t, filt.Signer) }, }, - {"transactions cosigner", + {"transactions signer", func(t *testing.T, wsc *WSClient) { - cosigner := util.Uint160{0, 42} - _, err := wsc.SubscribeForNewTransactions(nil, &cosigner) + signer := util.Uint160{0, 42} + _, err := wsc.SubscribeForNewTransactions(nil, &signer) require.NoError(t, err) }, func(t *testing.T, p *request.Params) { @@ -220,14 +220,14 @@ func TestWSFilteredSubscriptions(t *testing.T) { filt, ok := param.Value.(request.TxFilter) require.Equal(t, true, ok) require.Nil(t, filt.Sender) - require.Equal(t, util.Uint160{0, 42}, *filt.Cosigner) + require.Equal(t, util.Uint160{0, 42}, *filt.Signer) }, }, - {"transactions sender and cosigner", + {"transactions sender and signer", func(t *testing.T, wsc *WSClient) { sender := util.Uint160{1, 2, 3, 4, 5} - cosigner := util.Uint160{0, 42} - _, err := wsc.SubscribeForNewTransactions(&sender, &cosigner) + signer := util.Uint160{0, 42} + _, err := wsc.SubscribeForNewTransactions(&sender, &signer) require.NoError(t, err) }, func(t *testing.T, p *request.Params) { @@ -237,7 +237,7 @@ func TestWSFilteredSubscriptions(t *testing.T) { filt, ok := param.Value.(request.TxFilter) require.Equal(t, true, ok) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender) - require.Equal(t, util.Uint160{0, 42}, *filt.Cosigner) + require.Equal(t, util.Uint160{0, 42}, *filt.Signer) }, }, {"notifications", diff --git a/pkg/rpc/request/param.go b/pkg/rpc/request/param.go index 9f67b72004..324728dc0d 100644 --- a/pkg/rpc/request/param.go +++ b/pkg/rpc/request/param.go @@ -37,10 +37,10 @@ type ( Primary int `json:"primary"` } // TxFilter is a wrapper structure for transaction event filter. It - // allows to filter transactions by senders and cosigners. + // allows to filter transactions by senders and signers. TxFilter struct { - Sender *util.Uint160 `json:"sender,omitempty"` - Cosigner *util.Uint160 `json:"cosigner,omitempty"` + Sender *util.Uint160 `json:"sender,omitempty"` + Signer *util.Uint160 `json:"signer,omitempty"` } // NotificationFilter is a wrapper structure representing filter used for // notifications generated during transaction execution. Notifications can @@ -67,7 +67,7 @@ const ( TxFilterT NotificationFilterT ExecutionFilterT - Cosigner + Signer ) var errMissingParameter = errors.New("parameter is missing") @@ -207,24 +207,24 @@ func (p *Param) GetBytesBase64() ([]byte, error) { return base64.StdEncoding.DecodeString(s) } -// GetCosigner returns transaction.Cosigner value of the parameter. -func (p Param) GetCosigner() (transaction.Cosigner, error) { - c, ok := p.Value.(transaction.Cosigner) +// GetSigner returns transaction.Signer value of the parameter. +func (p Param) GetSigner() (transaction.Signer, error) { + c, ok := p.Value.(transaction.Signer) if !ok { - return transaction.Cosigner{}, errors.New("not a cosigner") + return transaction.Signer{}, errors.New("not a signer") } return c, nil } -// GetCosigners returns a slice of transaction.Cosigner with global scope from -// array of Uint160 or array of serialized transaction.Cosigner stored in the +// GetSigners returns a slice of transaction.Signer with global scope from +// array of Uint160 or array of serialized transaction.Signer stored in the // parameter. -func (p Param) GetCosigners() ([]transaction.Cosigner, error) { +func (p Param) GetSigners() ([]transaction.Signer, error) { hashes, err := p.GetArray() if err != nil { return nil, err } - cosigners := make([]transaction.Cosigner, len(hashes)) + signers := make([]transaction.Signer, len(hashes)) // try to extract hashes first for i, h := range hashes { var u util.Uint160 @@ -232,20 +232,20 @@ func (p Param) GetCosigners() ([]transaction.Cosigner, error) { if err != nil { break } - cosigners[i] = transaction.Cosigner{ + signers[i] = transaction.Signer{ Account: u, Scopes: transaction.Global, } } if err != nil { for i, h := range hashes { - cosigners[i], err = h.GetCosigner() + signers[i], err = h.GetSigner() if err != nil { return nil, err } } } - return cosigners, nil + return signers, nil } // UnmarshalJSON implements json.Unmarshaler interface. @@ -261,7 +261,7 @@ func (p *Param) UnmarshalJSON(data []byte) error { {TxFilterT, &TxFilter{}}, {NotificationFilterT, &NotificationFilter{}}, {ExecutionFilterT, &ExecutionFilter{}}, - {Cosigner, &transaction.Cosigner{}}, + {Signer, &transaction.Signer{}}, {ArrayT, &[]Param{}}, } @@ -296,7 +296,7 @@ func (p *Param) UnmarshalJSON(data []byte) error { } else { continue } - case *transaction.Cosigner: + case *transaction.Signer: p.Value = *val case *[]Param: p.Value = *val diff --git a/pkg/rpc/request/param_test.go b/pkg/rpc/request/param_test.go index e7443edc4f..497605eb05 100644 --- a/pkg/rpc/request/param_test.go +++ b/pkg/rpc/request/param_test.go @@ -18,8 +18,8 @@ func TestParam_UnmarshalJSON(t *testing.T) { msg := `["str1", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}], {"primary": 1}, {"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, - {"cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, - {"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554", "cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, + {"signer": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, + {"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554", "signer": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, {"contract": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, {"state": "HALT"}, {"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}, @@ -78,11 +78,11 @@ func TestParam_UnmarshalJSON(t *testing.T) { }, { Type: TxFilterT, - Value: TxFilter{Cosigner: &contr}, + Value: TxFilter{Signer: &contr}, }, { Type: TxFilterT, - Value: TxFilter{Sender: &contr, Cosigner: &contr}, + Value: TxFilter{Sender: &contr, Signer: &contr}, }, { Type: NotificationFilterT, @@ -93,8 +93,8 @@ func TestParam_UnmarshalJSON(t *testing.T) { Value: ExecutionFilter{State: "HALT"}, }, { - Type: Cosigner, - Value: transaction.Cosigner{ + Type: Signer, + Value: transaction.Signer{ Account: accountHash, Scopes: transaction.FeeOnly, }, @@ -103,8 +103,8 @@ func TestParam_UnmarshalJSON(t *testing.T) { Type: ArrayT, Value: []Param{ { - Type: Cosigner, - Value: transaction.Cosigner{ + Type: Signer, + Value: transaction.Signer{ Account: accountHash, Scopes: transaction.Global, }, @@ -280,22 +280,22 @@ func TestParamGetBytesBase64(t *testing.T) { require.NotNil(t, err) } -func TestParamGetCosigner(t *testing.T) { - c := transaction.Cosigner{ +func TestParamGetSigner(t *testing.T) { + c := transaction.Signer{ Account: util.Uint160{1, 2, 3, 4}, Scopes: transaction.Global, } - p := Param{Type: Cosigner, Value: c} - actual, err := p.GetCosigner() + p := Param{Type: Signer, Value: c} + actual, err := p.GetSigner() require.NoError(t, err) require.Equal(t, c, actual) - p = Param{Type: Cosigner, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`} - _, err = p.GetCosigner() + p = Param{Type: Signer, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`} + _, err = p.GetSigner() require.Error(t, err) } -func TestParamGetCosigners(t *testing.T) { +func TestParamGetSigners(t *testing.T) { u1 := util.Uint160{1, 2, 3, 4} u2 := util.Uint160{5, 6, 7, 8} t.Run("from hashes", func(t *testing.T) { @@ -303,19 +303,19 @@ func TestParamGetCosigners(t *testing.T) { {Type: StringT, Value: u1.StringLE()}, {Type: StringT, Value: u2.StringLE()}, }} - actual, err := p.GetCosigners() + actual, err := p.GetSigners() require.NoError(t, err) require.Equal(t, 2, len(actual)) require.True(t, u1.Equals(actual[0].Account)) require.True(t, u2.Equals(actual[1].Account)) }) - t.Run("from cosigners", func(t *testing.T) { - c1 := transaction.Cosigner{ + t.Run("from signers", func(t *testing.T) { + c1 := transaction.Signer{ Account: u1, Scopes: transaction.Global, } - c2 := transaction.Cosigner{ + c2 := transaction.Signer{ Account: u2, Scopes: transaction.CustomContracts, AllowedContracts: []util.Uint160{ @@ -324,10 +324,10 @@ func TestParamGetCosigners(t *testing.T) { }, } p := Param{ArrayT, []Param{ - {Type: Cosigner, Value: c1}, - {Type: Cosigner, Value: c2}, + {Type: Signer, Value: c1}, + {Type: Signer, Value: c2}, }} - actual, err := p.GetCosigners() + actual, err := p.GetSigners() require.NoError(t, err) require.Equal(t, 2, len(actual)) require.Equal(t, c1, actual[0]) @@ -339,7 +339,7 @@ func TestParamGetCosigners(t *testing.T) { {Type: StringT, Value: u1.StringLE()}, {Type: StringT, Value: "bla"}, }} - _, err := p.GetCosigners() + _, err := p.GetSigners() require.Error(t, err) }) } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 47873fdc73..add8073e56 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -843,13 +843,16 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons tx := &transaction.Transaction{} checkWitnessHashesIndex := len(reqParams) if checkWitnessHashesIndex > 3 { - cosigners, err := reqParams[3].GetCosigners() + signers, err := reqParams[3].GetSigners() if err != nil { return nil, response.ErrInvalidParams } - tx.Cosigners = cosigners + tx.Signers = signers checkWitnessHashesIndex-- } + if len(tx.Signers) == 0 { + tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.FeeOnly}} + } script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:checkWitnessHashesIndex]) if err != nil { return nil, response.NewInternalServerError("can't create invocation script", err) @@ -871,11 +874,14 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response. tx := &transaction.Transaction{} if len(reqParams) > 1 { - cosigners, err := reqParams[1].GetCosigners() + signers, err := reqParams[1].GetSigners() if err != nil { return nil, response.ErrInvalidParams } - tx.Cosigners = cosigners + tx.Signers = signers + } + if len(tx.Signers) == 0 { + tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.FeeOnly}} } tx.Script = script return s.runScriptInVM(script, tx), nil diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index acc15d1f4f..b3f3e7e5c1 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -52,7 +52,7 @@ type rpcTestCase struct { } const testContractHash = "36c3b0c85d98607db00b711885ec3e411d9b1672" -const deploymentTxHash = "ef4209bc06e1d8412995c645a8497d3e2c9a05ca52236de94297c6db9c3e94d0" +const deploymentTxHash = "60a1fc8ceb7948d9933aec0cedd148441568575c40af7e0985cc366ed153d57e" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { @@ -589,12 +589,12 @@ var rpcTestCases = map[string][]rpcTestCase{ "sendrawtransaction": { { name: "positive", - params: `["000a000000aa8acf859d4fe402b34e673f2156821796a488eb80969800000000009269130000000000b00400000001aa8acf859d4fe402b34e673f2156821796a488eb015d0300e87648170000000c1478ba4c24009fe510e136c9995a2e05215e1be4dc0c14aa8acf859d4fe402b34e673f2156821796a488eb13c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c4040719393aa590d962cb5a48e16360ac75a6c358c9699e9f1a853afede4d601b6783e28f5ec74542aaf59519e76830ba9d267656db324461fdb08d1d51521e103290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"]`, + params: `["000a0000008096980000000000721b130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005d0300e87648170000000c1478ba4c24009fe510e136c9995a2e05215e1be4dc0c14aa8acf859d4fe402b34e673f2156821796a488eb13c00c087472616e736665720c1425059ecb4878d3a875f91c51ceded330d4575fde41627d5b523801420c40b99503c74bb1861b0b45060501dd090224f6c404aca8c02ccba3243c9b9691c1ef9e6b824d731f8fab27c56ba75609d32d2d176e97f56d9e3780610c83ebd41a290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"]`, result: func(e *executor) interface{} { return &result.RelayResult{} }, check: func(t *testing.T, e *executor, inv interface{}) { res, ok := inv.(*result.RelayResult) require.True(t, ok) - expectedHash, err := util.Uint256DecodeStringLE("72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875") + expectedHash, err := util.Uint256DecodeStringLE("8b6e610a2205914411b26c4380594fa9a1e16961ff5896ed3b16831a151c6dd0") require.NoError(t, err) assert.Equal(t, expectedHash, res.Hash) }, @@ -744,7 +744,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) tx.Nonce = height + 1 tx.ValidUntilBlock = height + 10 - tx.Sender = acc0.PrivateKey().GetScriptHash() + tx.Signers = []transaction.Signer{{Account: acc0.PrivateKey().GetScriptHash()}} addNetworkFee(tx) require.NoError(t, acc0.SignTx(tx)) return tx @@ -872,6 +872,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] } for i := 0; i < 5; i++ { tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} assert.NoError(t, mp.Add(tx, &FeerStub{})) expected = append(expected, tx.Hash()) } @@ -1000,7 +1001,7 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "915.79002700", + Amount: "915.78962700", LastUpdated: 6, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), @@ -1101,7 +1102,7 @@ func checkNep5Transfers(t *testing.T, e *executor, acc interface{}) { b, err := e.chain.GetBlock(h) require.NoError(t, err) for j := range b.Transactions { - if u.Equals(b.Transactions[j].Sender) { + if u.Equals(b.Transactions[j].Sender()) { amount := b.Transactions[j].SystemFee + b.Transactions[j].NetworkFee expected.Sent = append(expected.Sent, result.NEP5Transfer{ Timestamp: b.Timestamp, diff --git a/pkg/rpc/server/subscription.go b/pkg/rpc/server/subscription.go index d4409e3e49..b404285604 100644 --- a/pkg/rpc/server/subscription.go +++ b/pkg/rpc/server/subscription.go @@ -58,18 +58,18 @@ func (f *feed) Matches(r *response.Notification) bool { case response.TransactionEventID: filt := f.filter.(request.TxFilter) tx := r.Payload[0].(*transaction.Transaction) - senderOK := filt.Sender == nil || tx.Sender.Equals(*filt.Sender) - cosignerOK := true - if filt.Cosigner != nil { - cosignerOK = false - for i := range tx.Cosigners { - if tx.Cosigners[i].Account.Equals(*filt.Cosigner) { - cosignerOK = true + senderOK := filt.Sender == nil || tx.Sender().Equals(*filt.Sender) + signerOK := true + if filt.Signer != nil { + signerOK = false + for i := range tx.Signers { + if tx.Signers[i].Account.Equals(*filt.Signer) { + signerOK = true break } } } - return senderOK && cosignerOK + return senderOK && signerOK case response.NotificationEventID: filt := f.filter.(request.NotificationFilter) notification := r.Payload[0].(result.NotificationEvent) diff --git a/pkg/rpc/server/subscription_test.go b/pkg/rpc/server/subscription_test.go index c4a81f9ca6..b92f3e80f3 100644 --- a/pkg/rpc/server/subscription_test.go +++ b/pkg/rpc/server/subscription_test.go @@ -147,28 +147,28 @@ func TestFilteredSubscriptions(t *testing.T) { require.Equal(t, address.Uint160ToString(goodSender), sender) }, }, - "tx matching cosigner": { - params: `["transaction_added", {"cosigner":"` + goodSender.StringLE() + `"}]`, + "tx matching signer": { + params: `["transaction_added", {"signer":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *response.Notification) { rmap := resp.Payload[0].(map[string]interface{}) require.Equal(t, response.TransactionEventID, resp.Event) - cosigners := rmap["cosigners"].([]interface{}) - cosigner0 := cosigners[0].(map[string]interface{}) - cosigner0acc := cosigner0["account"].(string) - require.Equal(t, "0x"+goodSender.StringLE(), cosigner0acc) + signers := rmap["signers"].([]interface{}) + signer0 := signers[0].(map[string]interface{}) + signer0acc := signer0["account"].(string) + require.Equal(t, "0x"+goodSender.StringLE(), signer0acc) }, }, - "tx matching sender and cosigner": { - params: `["transaction_added", {"sender":"` + goodSender.StringLE() + `", "cosigner":"` + goodSender.StringLE() + `"}]`, + "tx matching sender and signer": { + params: `["transaction_added", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `"}]`, check: func(t *testing.T, resp *response.Notification) { rmap := resp.Payload[0].(map[string]interface{}) require.Equal(t, response.TransactionEventID, resp.Event) sender := rmap["sender"].(string) require.Equal(t, address.Uint160ToString(goodSender), sender) - cosigners := rmap["cosigners"].([]interface{}) - cosigner0 := cosigners[0].(map[string]interface{}) - cosigner0acc := cosigner0["account"].(string) - require.Equal(t, "0x"+goodSender.StringLE(), cosigner0acc) + signers := rmap["signers"].([]interface{}) + signer0 := signers[0].(map[string]interface{}) + signer0acc := signer0["account"].(string) + require.Equal(t, "0x"+goodSender.StringLE(), signer0acc) }, }, "notification matching": { diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index 16abe186d63bc47e17f32a98054f9be6f0323d21..e34c40cd33d2b134f7426c85964d380bcec29d4a 100644 GIT binary patch delta 3411 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 KjqUu|FO3mq@v8E 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 Pl&C;4?rOWx?eqLUdE*Lm diff --git a/pkg/smartcontract/context/context_test.go b/pkg/smartcontract/context/context_test.go index 309135e794..302b5cda9e 100644 --- a/pkg/smartcontract/context/context_test.go +++ b/pkg/smartcontract/context/context_test.go @@ -169,6 +169,7 @@ func getContractTx() *transaction.Transaction { tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx.Attributes = make([]transaction.Attribute, 0) tx.Scripts = make([]transaction.Witness, 0) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} tx.Hash() return tx } From ead6bdb25ee8a4472288278f2aad04083e386f06 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 30 Jul 2020 14:32:30 +0300 Subject: [PATCH 4/5] core: remove unnecessary comment --- pkg/core/util_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 267e04fa25..dae606d641 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -17,10 +17,6 @@ func TestGenesisBlockMainNet(t *testing.T) { block, err := createGenesisBlock(cfg.ProtocolConfiguration) require.NoError(t, err) - //TODO: After we added Nonce field to transaction.Transaction, goveringTockenTx and UtilityTockenTx hashes - // have been changed. Consequently, hash of the genesis block has been changed. - // Update expected genesis block hash for better times. - // Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf" expect := "ecaee33262f1bc7c7c28f2b25b54a5d61d50670871f45c0c6fe755a40cbde4a8" assert.Equal(t, expect, block.Hash().StringLE()) } From ba08a9b6ad12119e1ff32150d0a22a8c84a083cf Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 30 Jul 2020 16:34:27 +0300 Subject: [PATCH 5/5] core: move all transaction.Decode checks to separate method We should perform the same checks during UnmarshalJSON. --- pkg/core/transaction/transaction.go | 72 ++++++++++++++--------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 948b313120..3b966386e1 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -123,50 +123,15 @@ func (t *Transaction) VerificationHash() util.Uint256 { // transaction, which are all fields except the scripts. func (t *Transaction) decodeHashableFields(br *io.BinReader) { t.Version = uint8(br.ReadB()) - if t.Version > 0 { - br.Err = errors.New("only version 0 is supported") - return - } t.Nonce = br.ReadU32LE() t.SystemFee = int64(br.ReadU64LE()) - if t.SystemFee < 0 { - br.Err = errors.New("negative system fee") - return - } t.NetworkFee = int64(br.ReadU64LE()) - if t.NetworkFee < 0 { - br.Err = errors.New("negative network fee") - return - } - if t.NetworkFee+t.SystemFee < t.SystemFee { - br.Err = errors.New("too big fees: int 64 overflow") - return - } t.ValidUntilBlock = br.ReadU32LE() - br.ReadArray(&t.Signers, MaxAttributes) - if len(t.Signers) == 0 { - br.Err = errors.New("signers array should contain sender") - return - } - for i := 0; i < len(t.Signers); i++ { - if i > 0 && t.Signers[i].Scopes == FeeOnly { - br.Err = errors.New("FeeOnly scope can be used only for sender") - return - } - for j := i + 1; j < len(t.Signers); j++ { - if t.Signers[i].Account.Equals(t.Signers[j].Account) { - br.Err = errors.New("transaction signers should be unique") - return - } - } - } - br.ReadArray(&t.Attributes, MaxAttributes-len(t.Signers)) t.Script = br.ReadVarBytes() - if br.Err == nil && len(t.Script) == 0 { - br.Err = errors.New("no script") - return + if br.Err == nil { + br.Err = t.isValid() } } @@ -354,5 +319,38 @@ func (t *Transaction) UnmarshalJSON(data []byte) error { return errors.New("txid doesn't match transaction hash") } + return t.isValid() +} + +// isValid checks whether decoded/unmarshalled transaction has all fields valid. +func (t *Transaction) isValid() error { + if t.Version > 0 { + return errors.New("only version 0 is supported") + } + if t.SystemFee < 0 { + return errors.New("negative system fee") + } + if t.NetworkFee < 0 { + return errors.New("negative network fee") + } + if t.NetworkFee+t.SystemFee < t.SystemFee { + return errors.New("too big fees: int64 overflow") + } + if len(t.Signers) == 0 { + return errors.New("signers array should contain sender") + } + for i := 0; i < len(t.Signers); i++ { + if i > 0 && t.Signers[i].Scopes == FeeOnly { + return errors.New("FeeOnly scope can be used only for sender") + } + for j := i + 1; j < len(t.Signers); j++ { + if t.Signers[i].Account.Equals(t.Signers[j].Account) { + return errors.New("transaction signers should be unique") + } + } + } + if len(t.Script) == 0 { + return errors.New("no script") + } return nil }