core: move transaction's sender to cosigners

Closes #1184

Ported changes from https://github.com/neo-project/neo/pull/1752
This commit is contained in:
Anna Shaleva 2020-07-29 19:57:38 +03:00
parent 8697582b23
commit 90825efa16
36 changed files with 307 additions and 264 deletions

View file

@ -148,11 +148,13 @@ func NewCommands() []cli.Command {
{ {
Name: "invokefunction", Name: "invokefunction",
Usage: "invoke deployed contract on the blockchain", 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, Description: `Executes given (as a script hash) deployed script with the given method,
arguments and cosigners. See testinvokefunction documentation for the details arguments and signers. Sender is included in the list of signers by default
about parameters. It differs from testinvokefunction in that this command with FeeOnly witness scope. If you'd like to change default sender's scope,
sends an invocation transaction to the network. 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, Action: invokeFunction,
Flags: invokeFunctionFlags, Flags: invokeFunctionFlags,
@ -160,14 +162,15 @@ func NewCommands() []cli.Command {
{ {
Name: "testinvokefunction", Name: "testinvokefunction",
Usage: "invoke deployed contract on the blockchain (test mode)", 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, 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 arguments and signers (sender is not included by default). If no method is given
no arguments are given, an empty array is passed, if no cosigners are given, "" is passed to the script, if no arguments are given, an empty array is
no array will be passed. All of the given arguments are encapsulated into passed, if no signers are given no array is passed. If signers are specified,
array before invoking the script. The script thus should follow the regular the first one of them is treated as a sender. All of the given arguments are
convention of smart contract arguments (method string and an array of other encapsulated into array before invoking the script. The script thus should
arguments). 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 Arguments always do have regular Neo smart contract parameter types, either
specified explicitly or being inferred from the value. To specify the type specified explicitly or being inferred from the value. To specify the type
@ -224,12 +227,15 @@ func NewCommands() []cli.Command {
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a * '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
Cosigners represent a set of Uint160 hashes with witness scopes and are used Signers represent a set of Uint160 hashes with witness scopes and are used
to verify hashes in System.Runtime.CheckWitness syscall. To specify cosigners to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated
use cosigner[:scope] syntax where as a sender. To specify signers use signer[:scope] syntax where
* 'cosigner' is hex-encoded 160 bit (20 byte) LE value of cosigner's address, * 'signer' is hex-encoded 160 bit (20 byte) LE value of signer's address,
which could have '0x' prefix. which could have '0x' prefix.
* 'scope' is a comma-separated set of cosigner's scopes, which could be: * '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 - 'Global' - allows this witness in all contexts. This cannot be combined
with other flags. with other flags.
- 'CalledByEntry' - means that this condition must hold: EntryScriptHash - '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. - 'CustomContracts' - define valid custom contract hashes for witness check.
- 'CustomGroups' - define custom pubkey for group members. - 'CustomGroups' - define custom pubkey for group members.
If no scopes were specified, 'Global' used as default. If no cosigners were If no scopes were specified, 'Global' used as default. If no signers were
specified, no array will be passed. Note that scopes are properly handled by specified, no array is passed. Note that scopes are properly handled by
neo-go RPC server only. C# implementation does not support scopes capability. neo-go RPC server only. C# implementation does not support scopes capability.
Examples: Examples:
@ -256,9 +262,10 @@ func NewCommands() []cli.Command {
{ {
Name: "testinvokescript", Name: "testinvokescript",
Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)", 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 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, Action: testInvokeScript,
Flags: testInvokeScriptFlags, Flags: testInvokeScriptFlags,
@ -389,7 +396,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
operation string operation string
params = make([]smartcontract.Parameter, 0) params = make([]smartcontract.Parameter, 0)
paramsStart = 1 paramsStart = 1
cosigners []transaction.Cosigner cosigners []transaction.Signer
cosignersStart = 0 cosignersStart = 0
resp *result.Invoke resp *result.Invoke
acc *wallet.Account acc *wallet.Account
@ -493,14 +500,14 @@ func testInvokeScript(ctx *cli.Context) error {
} }
args := ctx.Args() args := ctx.Args()
var cosigners []transaction.Cosigner var signers []transaction.Signer
if args.Present() { if args.Present() {
for i, c := range args[:] { for i, c := range args[:] {
cosigner, err := parseCosigner(c) cosigner, err := parseCosigner(c)
if err != nil { 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 return err
} }
resp, err := c.InvokeScript(nefFile.Script, cosigners) resp, err := c.InvokeScript(nefFile.Script, signers)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
@ -659,7 +666,7 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1) 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) invRes, err := c.InvokeScript(txScript, nil)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to test-invoke deployment script: %v", err), 1) 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 return conf, nil
} }
func parseCosigner(c string) (transaction.Cosigner, error) { func parseCosigner(c string) (transaction.Signer, error) {
var ( var (
err error err error
res = transaction.Cosigner{ res = transaction.Signer{
Scopes: transaction.Global, Scopes: transaction.Global,
} }
) )
@ -706,7 +713,7 @@ func parseCosigner(c string) (transaction.Cosigner, error) {
if len(data) > 1 { if len(data) > 1 {
res.Scopes, err = transaction.ScopesFromString(data[1]) res.Scopes, err = transaction.ScopesFromString(data[1])
if err != nil { if err != nil {
return transaction.Cosigner{}, err return transaction.Signer{}, err
} }
} }
return res, nil return res, nil

View file

@ -13,7 +13,7 @@ Currently supported events:
Filters: primary ID. Filters: primary ID.
* new transaction in the block * new transaction in the block
Contents: transaction. Contents: transaction.
Filters: sender and cosigner. Filters: sender and signer.
* notification generated during execution * notification generated during execution
Contents: container hash, contract script hash, stack item. Contents: container hash, contract script hash, stack item.
Filters: contract script hash. Filters: contract script hash.
@ -57,8 +57,8 @@ Recognized stream names:
ConsensusData. ConsensusData.
* `transaction_added` * `transaction_added`
Filter: `sender` field containing string with hex-encoded Uint160 (LE Filter: `sender` field containing string with hex-encoded Uint160 (LE
representation) for transaction's `Sender` and/or `cosigner` in the same representation) for transaction's `Sender` and/or `signer` in the same
format for one of transaction's `Cosigners`. format for one of transaction's `Signers`.
* `notification_from_execution` * `notification_from_execution`
Filter: `contract` field containing string with hex-encoded Uint160 (LE Filter: `contract` field containing string with hex-encoded Uint160 (LE
representation). representation).
@ -203,7 +203,7 @@ Example:
"sysfee" : "0", "sysfee" : "0",
"type" : "InvocationTransaction", "type" : "InvocationTransaction",
"nonce" : 9, "nonce" : 9,
"cosigners" : [ "signers" : [
{ {
"scopes" : 1, "scopes" : 1,
"account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31" "account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31"
@ -262,7 +262,7 @@ Example:
"nonce" : 9, "nonce" : 9,
"vin" : [], "vin" : [],
"type" : "InvocationTransaction", "type" : "InvocationTransaction",
"cosigners" : [ "signers" : [
{ {
"account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31", "account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31",
"scopes" : 1 "scopes" : 1

View file

@ -80,7 +80,12 @@ func getTX(t *testing.B, wif *keys.WIF) *transaction.Transaction {
tx := transaction.New(netmode.UnitTestNet, []byte{0x51}, 1) tx := transaction.New(netmode.UnitTestNet, []byte{0x51}, 1)
tx.Version = 0 tx.Version = 0
tx.Sender = fromAddressHash tx.Signers = []transaction.Signer{
{
Account: fromAddressHash,
Scopes: transaction.FeeOnly,
},
}
tx.Attributes = append(tx.Attributes, tx.Attributes = append(tx.Attributes,
transaction.Attribute{ transaction.Attribute{
Usage: transaction.DescriptionURL, Usage: transaction.DescriptionURL,

View file

@ -246,7 +246,11 @@ var neoOwner = testchain.MultisigScriptHash()
func addSender(t *testing.T, txs ...*transaction.Transaction) { func addSender(t *testing.T, txs ...*transaction.Transaction) {
for _, tx := range txs { for _, tx := range txs {
tx.Sender = neoOwner tx.Signers = []transaction.Signer{
{
Account: neoOwner,
},
}
} }
} }

View file

@ -1226,7 +1226,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
if maxBlockSystemFee < t.SystemFee { if maxBlockSystemFee < t.SystemFee {
return errors.Errorf("policy check failed: transaction's fee shouldn't exceed maximum block system fee %d", maxBlockSystemFee) 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 need := t.SystemFee + t.NetworkFee
if balance.Cmp(big.NewInt(need)) < 0 { if balance.Cmp(big.NewInt(need)) < 0 {
return errors.Errorf("insufficient funds: balance is %v, need: %v", balance, need) 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. // 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) // 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) { func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([]util.Uint160, error) {
hashes := make(map[util.Uint160]bool) hashesResult := make([]util.Uint160, len(t.Signers))
hashes[t.Sender] = true for i, s := range t.Signers {
for _, c := range t.Cosigners { hashesResult[i] = s.Account
hashes[c.Account] = true
}
// convert hashes to []util.Uint160
hashesResult := make([]util.Uint160, 0, len(hashes))
for h := range hashes {
hashesResult = append(hashesResult, h)
} }
return hashesResult, nil return hashesResult, nil

View file

@ -107,7 +107,7 @@ func TestGetHeader(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.ValidUntilBlock = bc.BlockHeight() + 1 tx.ValidUntilBlock = bc.BlockHeight() + 1
assert.Nil(t, addSender(tx)) addSigners(tx)
assert.Nil(t, signTx(bc, tx)) assert.Nil(t, signTx(bc, tx))
block := bc.newBlock(tx) block := bc.newBlock(tx)
err := bc.AddBlock(block) err := bc.AddBlock(block)
@ -276,7 +276,7 @@ func TestSubscriptions(t *testing.T) {
emit.Syscall(script.BinWriter, "System.Runtime.Notify") emit.Syscall(script.BinWriter, "System.Runtime.Notify")
require.NoError(t, script.Err) require.NoError(t, script.Err)
txGood1 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0) txGood1 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
txGood1.Sender = neoOwner txGood1.Signers = []transaction.Signer{{Account: neoOwner}}
txGood1.Nonce = 1 txGood1.Nonce = 1
txGood1.ValidUntilBlock = 100500 txGood1.ValidUntilBlock = 100500
require.NoError(t, signTx(bc, txGood1)) require.NoError(t, signTx(bc, txGood1))
@ -288,7 +288,7 @@ func TestSubscriptions(t *testing.T) {
emit.Opcode(script.BinWriter, opcode.THROW) emit.Opcode(script.BinWriter, opcode.THROW)
require.NoError(t, script.Err) require.NoError(t, script.Err)
txBad := transaction.New(netmode.UnitTestNet, script.Bytes(), 0) txBad := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
txBad.Sender = neoOwner txBad.Signers = []transaction.Signer{{Account: neoOwner}}
txBad.Nonce = 2 txBad.Nonce = 2
txBad.ValidUntilBlock = 100500 txBad.ValidUntilBlock = 100500
require.NoError(t, signTx(bc, txBad)) require.NoError(t, signTx(bc, txBad))
@ -298,7 +298,7 @@ func TestSubscriptions(t *testing.T) {
emit.Syscall(script.BinWriter, "System.Runtime.Notify") emit.Syscall(script.BinWriter, "System.Runtime.Notify")
require.NoError(t, script.Err) require.NoError(t, script.Err)
txGood2 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0) txGood2 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
txGood2.Sender = neoOwner txGood2.Signers = []transaction.Signer{{Account: neoOwner}}
txGood2.Nonce = 3 txGood2.Nonce = 3
txGood2.ValidUntilBlock = 100500 txGood2.ValidUntilBlock = 100500
require.NoError(t, signTx(bc, txGood2)) require.NoError(t, signTx(bc, txGood2))

View file

@ -190,8 +190,7 @@ func TestCreateBasicChain(t *testing.T) {
txMoveNeo := newNEP5Transfer(neoHash, neoOwner, priv0ScriptHash, neoAmount) txMoveNeo := newNEP5Transfer(neoHash, neoOwner, priv0ScriptHash, neoAmount)
txMoveNeo.ValidUntilBlock = validUntilBlock txMoveNeo.ValidUntilBlock = validUntilBlock
txMoveNeo.Nonce = getNextNonce() txMoveNeo.Nonce = getNextNonce()
txMoveNeo.Sender = neoOwner txMoveNeo.Signers = []transaction.Signer{{
txMoveNeo.Cosigners = []transaction.Cosigner{{
Account: neoOwner, Account: neoOwner,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
AllowedContracts: nil, AllowedContracts: nil,
@ -202,8 +201,7 @@ func TestCreateBasicChain(t *testing.T) {
txMoveGas := newNEP5Transfer(gasHash, neoOwner, priv0ScriptHash, int64(util.Fixed8FromInt64(1000))) txMoveGas := newNEP5Transfer(gasHash, neoOwner, priv0ScriptHash, int64(util.Fixed8FromInt64(1000)))
txMoveGas.ValidUntilBlock = validUntilBlock txMoveGas.ValidUntilBlock = validUntilBlock
txMoveGas.Nonce = getNextNonce() txMoveGas.Nonce = getNextNonce()
txMoveGas.Sender = neoOwner txMoveGas.Signers = []transaction.Signer{{
txMoveGas.Cosigners = []transaction.Cosigner{{
Account: neoOwner, Account: neoOwner,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
AllowedContracts: nil, AllowedContracts: nil,
@ -212,8 +210,14 @@ func TestCreateBasicChain(t *testing.T) {
require.NoError(t, signTx(bc, txMoveGas)) require.NoError(t, signTx(bc, txMoveGas))
b := bc.newBlock(txMoveNeo, txMoveGas) b := bc.newBlock(txMoveNeo, txMoveGas)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE()) t.Logf("Block1 hash: %s", b.Hash().StringLE())
t.Logf("txMoveGas: %s", txMoveGas.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) require.True(t, bc.GetUtilityTokenBalance(priv0ScriptHash).Cmp(big.NewInt(1000*native.GASFactor)) >= 0)
// info for getblockheader rpc tests // info for getblockheader rpc tests
@ -245,12 +249,13 @@ func TestCreateBasicChain(t *testing.T) {
txDeploy := transaction.New(testchain.Network(), txScript, 100*native.GASFactor) txDeploy := transaction.New(testchain.Network(), txScript, 100*native.GASFactor)
txDeploy.Nonce = getNextNonce() txDeploy.Nonce = getNextNonce()
txDeploy.ValidUntilBlock = validUntilBlock txDeploy.ValidUntilBlock = validUntilBlock
txDeploy.Sender = priv0ScriptHash txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, txDeploy, acc0)) require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
require.NoError(t, acc0.SignTx(txDeploy)) require.NoError(t, acc0.SignTx(txDeploy))
b = bc.newBlock(txDeploy) b = bc.newBlock(txDeploy)
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
t.Logf("txDeploy: %s", txDeploy.Hash().StringLE()) t.Logf("txDeploy: %s", txDeploy.Hash().StringLE())
t.Logf("Block2 hash: %s", b.Hash().StringLE())
// Now invoke this contract. // Now invoke this contract.
script = io.NewBufBinWriter() script = io.NewBufBinWriter()
@ -259,7 +264,7 @@ func TestCreateBasicChain(t *testing.T) {
txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor) txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
txInv.Nonce = getNextNonce() txInv.Nonce = getNextNonce()
txInv.ValidUntilBlock = validUntilBlock txInv.ValidUntilBlock = validUntilBlock
txInv.Sender = priv0ScriptHash txInv.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, txInv, acc0)) require.NoError(t, addNetworkFee(bc, txInv, acc0))
require.NoError(t, acc0.SignTx(txInv)) require.NoError(t, acc0.SignTx(txInv))
b = bc.newBlock(txInv) b = bc.newBlock(txInv)
@ -270,8 +275,7 @@ func TestCreateBasicChain(t *testing.T) {
txNeo0to1 := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), 1000) txNeo0to1 := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), 1000)
txNeo0to1.Nonce = getNextNonce() txNeo0to1.Nonce = getNextNonce()
txNeo0to1.ValidUntilBlock = validUntilBlock txNeo0to1.ValidUntilBlock = validUntilBlock
txNeo0to1.Sender = priv0ScriptHash txNeo0to1.Signers = []transaction.Signer{
txNeo0to1.Cosigners = []transaction.Cosigner{
{ {
Account: priv0ScriptHash, Account: priv0ScriptHash,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
@ -290,14 +294,13 @@ func TestCreateBasicChain(t *testing.T) {
initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor) initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor)
initTx.Nonce = getNextNonce() initTx.Nonce = getNextNonce()
initTx.ValidUntilBlock = validUntilBlock initTx.ValidUntilBlock = validUntilBlock
initTx.Sender = priv0ScriptHash initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, initTx, acc0)) require.NoError(t, addNetworkFee(bc, initTx, acc0))
require.NoError(t, acc0.SignTx(initTx)) require.NoError(t, acc0.SignTx(initTx))
transferTx := newNEP5Transfer(sh, sh, priv0.GetScriptHash(), 1000) transferTx := newNEP5Transfer(sh, sh, priv0.GetScriptHash(), 1000)
transferTx.Nonce = getNextNonce() transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock transferTx.ValidUntilBlock = validUntilBlock
transferTx.Sender = priv0ScriptHash transferTx.Signers = []transaction.Signer{
transferTx.Cosigners = []transaction.Cosigner{
{ {
Account: priv0ScriptHash, Account: priv0ScriptHash,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
@ -315,8 +318,7 @@ func TestCreateBasicChain(t *testing.T) {
transferTx = newNEP5Transfer(sh, priv0.GetScriptHash(), priv1.GetScriptHash(), 123) transferTx = newNEP5Transfer(sh, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
transferTx.Nonce = getNextNonce() transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock transferTx.ValidUntilBlock = validUntilBlock
transferTx.Sender = priv0ScriptHash transferTx.Signers = []transaction.Signer{
transferTx.Cosigners = []transaction.Cosigner{
{ {
Account: priv0ScriptHash, Account: priv0ScriptHash,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
@ -357,8 +359,7 @@ func TestCreateBasicChain(t *testing.T) {
txSendRaw := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), int64(util.Fixed8FromInt64(1000))) txSendRaw := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), int64(util.Fixed8FromInt64(1000)))
txSendRaw.ValidUntilBlock = validUntilBlock txSendRaw.ValidUntilBlock = validUntilBlock
txSendRaw.Nonce = getNextNonce() txSendRaw.Nonce = getNextNonce()
txSendRaw.Sender = priv0ScriptHash txSendRaw.Signers = []transaction.Signer{{
txSendRaw.Cosigners = []transaction.Cosigner{{
Account: priv0ScriptHash, Account: priv0ScriptHash,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
AllowedContracts: nil, AllowedContracts: nil,
@ -366,7 +367,7 @@ func TestCreateBasicChain(t *testing.T) {
}} }}
require.NoError(t, addNetworkFee(bc, txSendRaw, acc0)) require.NoError(t, addNetworkFee(bc, txSendRaw, acc0))
require.NoError(t, acc0.SignTx(txSendRaw)) require.NoError(t, acc0.SignTx(txSendRaw))
bw := io.NewBufBinWriter() bw = io.NewBufBinWriter()
txSendRaw.EncodeBinary(bw.BinWriter) txSendRaw.EncodeBinary(bw.BinWriter)
t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes())) 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) return transaction.New(testchain.Network(), script, 10000000)
} }
func addSender(txs ...*transaction.Transaction) error { func addSigners(txs ...*transaction.Transaction) {
for _, tx := range txs { for _, tx := range txs {
tx.Sender = neoOwner tx.Signers = []transaction.Signer{{
}
return nil
}
func addCosigners(txs ...*transaction.Transaction) {
for _, tx := range txs {
tx.Cosigners = []transaction.Cosigner{{
Account: neoOwner, Account: neoOwner,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
AllowedContracts: nil, AllowedContracts: nil,
@ -424,7 +418,7 @@ func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.A
netFee, sizeDelta := CalculateNetworkFee(sender.Contract.Script) netFee, sizeDelta := CalculateNetworkFee(sender.Contract.Script)
tx.NetworkFee += netFee tx.NetworkFee += netFee
size += sizeDelta size += sizeDelta
for _, cosigner := range tx.Cosigners { for _, cosigner := range tx.Signers {
contract := bc.GetContractState(cosigner.Account) contract := bc.GetContractState(cosigner.Account)
if contract != nil { if contract != nil {
netFee, sizeDelta = CalculateNetworkFee(contract.Script) netFee, sizeDelta = CalculateNetworkFee(contract.Script)

View file

@ -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) { 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.Account == hash {
if c.Scopes == transaction.Global { if c.Scopes == transaction.Global {
return true, nil return true, nil

View file

@ -324,6 +324,7 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Con
}) })
tx.Attributes = attributes tx.Attributes = attributes
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3, 4}}}
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, tx)
return v, tx, context, chain return v, tx, context, chain

View file

@ -167,7 +167,7 @@ func transactionToStackItem(t *transaction.Transaction) stackitem.Item {
stackitem.NewByteArray(t.Hash().BytesBE()), stackitem.NewByteArray(t.Hash().BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(t.Version))), stackitem.NewBigInteger(big.NewInt(int64(t.Version))),
stackitem.NewBigInteger(big.NewInt(int64(t.Nonce))), 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.SystemFee))),
stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))), stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))),
stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))), stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))),

View file

@ -39,7 +39,7 @@ func TestBCGetTransaction(t *testing.T) {
require.Equal(t, tx.Hash().BytesBE(), actual[0].Value().([]byte)) 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.Version), actual[1].Value().(*big.Int).Int64())
require.Equal(t, int64(tx.Nonce), actual[2].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.SystemFee), actual[4].Value().(*big.Int).Int64())
require.Equal(t, int64(tx.NetworkFee), actual[5].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()) require.Equal(t, int64(tx.ValidUntilBlock), actual[6].Value().(*big.Int).Int64())

View file

@ -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 // 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 // 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 { 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 { if !ok {
senderFee.balance = feer.GetUtilityTokenBalance(tx.Sender) senderFee.balance = feer.GetUtilityTokenBalance(tx.Sender())
mp.fees[tx.Sender] = senderFee mp.fees[tx.Sender()] = senderFee
} }
if needCheck && !checkBalance(tx, senderFee) { if needCheck && !checkBalance(tx, senderFee) {
return false return false
} }
senderFee.feeSum += tx.SystemFee + tx.NetworkFee senderFee.feeSum += tx.SystemFee + tx.NetworkFee
mp.fees[tx.Sender] = senderFee mp.fees[tx.Sender()] = senderFee
return true return true
} }
@ -199,9 +199,9 @@ func (mp *Pool) Remove(hash util.Uint256) {
} else if num == len(mp.verifiedTxes)-1 { } else if num == len(mp.verifiedTxes)-1 {
mp.verifiedTxes = mp.verifiedTxes[:num] 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 senderFee.feeSum -= it.txn.SystemFee + it.txn.NetworkFee
mp.fees[it.txn.Sender] = senderFee mp.fees[it.txn.Sender()] = senderFee
} }
updateMempoolMetrics(len(mp.verifiedTxes)) updateMempoolMetrics(len(mp.verifiedTxes))
mp.lock.Unlock() mp.lock.Unlock()
@ -284,9 +284,9 @@ func (mp *Pool) GetVerifiedTransactions() []*transaction.Transaction {
// checkTxConflicts is an internal unprotected version of Verify. // checkTxConflicts is an internal unprotected version of Verify.
func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool { func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool {
senderFee, ok := mp.fees[tx.Sender] senderFee, ok := mp.fees[tx.Sender()]
if !ok { if !ok {
senderFee.balance = fee.GetUtilityTokenBalance(tx.Sender) senderFee.balance = fee.GetUtilityTokenBalance(tx.Sender())
} }
return checkBalance(tx, senderFee) return checkBalance(tx, senderFee)
} }

View file

@ -31,6 +31,7 @@ func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
mp := NewMemPool(10) mp := NewMemPool(10)
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = 0 tx.Nonce = 0
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
_, ok := mp.TryGetValue(tx.Hash()) _, ok := mp.TryGetValue(tx.Hash())
require.Equal(t, false, ok) require.Equal(t, false, ok)
require.NoError(t, mp.Add(tx, fs)) require.NoError(t, mp.Add(tx, fs))
@ -60,6 +61,7 @@ func TestOverCapacity(t *testing.T) {
for i := 0; i < mempoolSize; i++ { for i := 0; i < mempoolSize; i++ {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = uint32(i) tx.Nonce = uint32(i)
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
require.NoError(t, mp.Add(tx, fs)) require.NoError(t, mp.Add(tx, fs))
} }
txcnt := uint32(mempoolSize) txcnt := uint32(mempoolSize)
@ -75,6 +77,7 @@ func TestOverCapacity(t *testing.T) {
}) })
tx.NetworkFee = 10000 tx.NetworkFee = 10000
tx.Nonce = txcnt tx.Nonce = txcnt
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
txcnt++ txcnt++
// size is 84, networkFee is 10000 => feePerByte is 119 // size is 84, networkFee is 10000 => feePerByte is 119
require.NoError(t, mp.Add(tx, fs)) require.NoError(t, mp.Add(tx, fs))
@ -89,6 +92,7 @@ func TestOverCapacity(t *testing.T) {
}) })
tx.NetworkFee = 100 tx.NetworkFee = 100
tx.Nonce = txcnt tx.Nonce = txcnt
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
txcnt++ txcnt++
require.Error(t, mp.Add(tx, fs)) require.Error(t, mp.Add(tx, fs))
require.Equal(t, mempoolSize, mp.Count()) 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 = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = txcnt tx.Nonce = txcnt
tx.NetworkFee = 7000 tx.NetworkFee = 7000
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
txcnt++ txcnt++
// size is 51 (no attributes), networkFee is 7000 (<10000) // size is 51 (no attributes), networkFee is 7000 (<10000)
// => feePerByte is 137 (>119) // => feePerByte is 137 (>119)
@ -110,6 +115,7 @@ func TestOverCapacity(t *testing.T) {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.NetworkFee = 8000 tx.NetworkFee = 8000
tx.Nonce = txcnt tx.Nonce = txcnt
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
txcnt++ txcnt++
require.NoError(t, mp.Add(tx, fs)) require.NoError(t, mp.Add(tx, fs))
require.Equal(t, mempoolSize, mp.Count()) 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 = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = txcnt tx.Nonce = txcnt
tx.NetworkFee = 7000 tx.NetworkFee = 7000
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
require.Error(t, mp.Add(tx, fs)) require.Error(t, mp.Add(tx, fs))
require.Equal(t, mempoolSize, mp.Count()) require.Equal(t, mempoolSize, mp.Count())
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes))) 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++ { for i := 0; i < mempoolSize; i++ {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = uint32(i) tx.Nonce = uint32(i)
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
txes = append(txes, tx) txes = append(txes, tx)
require.NoError(t, mp.Add(tx, fs)) require.NoError(t, mp.Add(tx, fs))
} }
@ -157,6 +165,7 @@ func TestRemoveStale(t *testing.T) {
for i := 0; i < mempoolSize; i++ { for i := 0; i < mempoolSize; i++ {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = uint32(i) tx.Nonce = uint32(i)
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
if i%2 == 0 { if i%2 == 0 {
txes1 = append(txes1, tx) txes1 = append(txes1, tx)
} else { } else {
@ -186,7 +195,7 @@ func TestMemPoolFees(t *testing.T) {
sender0 := util.Uint160{1, 2, 3} sender0 := util.Uint160{1, 2, 3}
tx0 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx0 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx0.NetworkFee = balance.Int64() + 1 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 // insufficient funds to add transaction, and balance shouldn't be stored
require.Equal(t, false, mp.Verify(tx0, &FeerStub{})) require.Equal(t, false, mp.Verify(tx0, &FeerStub{}))
require.Error(t, mp.Add(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 // no problems with adding another transaction with lower fee
tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx1.NetworkFee = balancePart.Int64() tx1.NetworkFee = balancePart.Int64()
tx1.Sender = sender0 tx1.Signers = []transaction.Signer{{Account: sender0}}
require.NoError(t, mp.Add(tx1, &FeerStub{})) require.NoError(t, mp.Add(tx1, &FeerStub{}))
require.Equal(t, 1, len(mp.fees)) require.Equal(t, 1, len(mp.fees))
require.Equal(t, utilityBalanceAndFees{ require.Equal(t, utilityBalanceAndFees{
@ -207,7 +216,7 @@ func TestMemPoolFees(t *testing.T) {
// balance shouldn't change after adding one more transaction // balance shouldn't change after adding one more transaction
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx2.NetworkFee = new(big.Int).Sub(balance, balancePart).Int64() 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.NoError(t, mp.Add(tx2, &FeerStub{}))
require.Equal(t, 2, len(mp.verifiedTxes)) require.Equal(t, 2, len(mp.verifiedTxes))
require.Equal(t, 1, len(mp.fees)) 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 // can't add more transactions as we don't have enough GAS
tx3 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx3 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx3.NetworkFee = 1 tx3.NetworkFee = 1
tx3.Sender = sender0 tx3.Signers = []transaction.Signer{{Account: sender0}}
require.Equal(t, false, mp.Verify(tx3, &FeerStub{})) require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
require.Error(t, mp.Add(tx3, &FeerStub{})) require.Error(t, mp.Add(tx3, &FeerStub{}))
require.Equal(t, 1, len(mp.fees)) require.Equal(t, 1, len(mp.fees))

View file

@ -88,7 +88,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
} }
for _, tx := range ic.Block.Transactions { for _, tx := range ic.Block.Transactions {
absAmount := big.NewInt(tx.SystemFee + tx.NetworkFee) 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) validators, err := g.NEO.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
if err != nil { if err != nil {

View file

@ -115,13 +115,13 @@ func TestNativeContract_Invoke(t *testing.T) {
tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*2+10000) tx := transaction.New(chain.GetConfig().Magic, script, testSumPrice*2+10000)
validUntil := chain.blockHeight + 1 validUntil := chain.blockHeight + 1
tx.ValidUntilBlock = validUntil tx.ValidUntilBlock = validUntil
require.NoError(t, addSender(tx)) addSigners(tx)
require.NoError(t, signTx(chain, tx)) require.NoError(t, signTx(chain, tx))
// Enough for Call and other opcodes, but not enough for "sum" call. // Enough for Call and other opcodes, but not enough for "sum" call.
tx2 := transaction.New(chain.GetConfig().Magic, script, testSumPrice*2) tx2 := transaction.New(chain.GetConfig().Magic, script, testSumPrice*2)
tx2.ValidUntilBlock = chain.blockHeight + 1 tx2.ValidUntilBlock = chain.blockHeight + 1
require.NoError(t, addSender(tx2)) addSigners(tx2)
require.NoError(t, signTx(chain, tx2)) require.NoError(t, signTx(chain, tx2))
b := chain.newBlock(tx, tx2) b := chain.newBlock(tx, tx2)

View file

@ -225,12 +225,8 @@ func invokeNativePolicyMethod(chain *Blockchain, method string, args ...interfac
tx := transaction.New(chain.GetConfig().Magic, script, 10000000) tx := transaction.New(chain.GetConfig().Magic, script, 10000000)
validUntil := chain.blockHeight + 1 validUntil := chain.blockHeight + 1
tx.ValidUntilBlock = validUntil tx.ValidUntilBlock = validUntil
err := addSender(tx) addSigners(tx)
if err != nil { err := signTx(chain, tx)
return nil, err
}
addCosigners(tx)
err = signTx(chain, tx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -11,8 +11,8 @@ import (
// The maximum number of AllowedContracts or AllowedGroups // The maximum number of AllowedContracts or AllowedGroups
const maxSubitems = 16 const maxSubitems = 16
// Cosigner implements a Transaction cosigner. // Signer implements a Transaction signer.
type Cosigner struct { type Signer struct {
Account util.Uint160 `json:"account"` Account util.Uint160 `json:"account"`
Scopes WitnessScope `json:"scopes"` Scopes WitnessScope `json:"scopes"`
AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"` AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"`
@ -20,7 +20,7 @@ type Cosigner struct {
} }
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (c *Cosigner) EncodeBinary(bw *io.BinWriter) { func (c *Signer) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(c.Account[:]) bw.WriteBytes(c.Account[:])
bw.WriteB(byte(c.Scopes)) bw.WriteB(byte(c.Scopes))
if c.Scopes&CustomContracts != 0 { if c.Scopes&CustomContracts != 0 {
@ -32,10 +32,10 @@ func (c *Cosigner) EncodeBinary(bw *io.BinWriter) {
} }
// DecodeBinary implements Serializable interface. // DecodeBinary implements Serializable interface.
func (c *Cosigner) DecodeBinary(br *io.BinReader) { func (c *Signer) DecodeBinary(br *io.BinReader) {
br.ReadBytes(c.Account[:]) br.ReadBytes(c.Account[:])
c.Scopes = WitnessScope(br.ReadB()) 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") br.Err = errors.New("unknown witness scope")
return return
} }

View file

@ -8,21 +8,21 @@ import (
) )
func TestCosignerEncodeDecode(t *testing.T) { func TestCosignerEncodeDecode(t *testing.T) {
expected := &Cosigner{ expected := &Signer{
Account: util.Uint160{1, 2, 3, 4, 5}, Account: util.Uint160{1, 2, 3, 4, 5},
Scopes: CustomContracts, Scopes: CustomContracts,
AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}}, AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}},
} }
actual := &Cosigner{} actual := &Signer{}
testserdes.EncodeDecodeBinary(t, expected, actual) testserdes.EncodeDecodeBinary(t, expected, actual)
} }
func TestCosignerMarshallUnmarshallJSON(t *testing.T) { func TestCosignerMarshallUnmarshallJSON(t *testing.T) {
expected := &Cosigner{ expected := &Signer{
Account: util.Uint160{1, 2, 3, 4, 5}, Account: util.Uint160{1, 2, 3, 4, 5},
Scopes: CustomContracts, Scopes: CustomContracts,
AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}}, AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}},
} }
actual := &Cosigner{} actual := &Signer{}
testserdes.MarshalUnmarshalJSON(t, expected, actual) testserdes.MarshalUnmarshalJSON(t, expected, actual)
} }

View file

@ -19,9 +19,9 @@ const (
// MaxValidUntilBlockIncrement is the upper increment size of blockhain height in blocs after // 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. // exceeding that a transaction should fail validation. It is set to be 2102400.
MaxValidUntilBlockIncrement = 2102400 MaxValidUntilBlockIncrement = 2102400
// MaxCosigners is maximum number of cosigners that can be contained within a transaction. // MaxAttributes is maximum number of attributes including signers that can be contained
// It is set to be 16. // within a transaction. It is set to be 16.
MaxCosigners = 16 MaxAttributes = 16
) )
// Transaction is a process recorded in the NEO blockchain. // Transaction is a process recorded in the NEO blockchain.
@ -32,9 +32,6 @@ type Transaction struct {
// Random number to avoid hash collision. // Random number to avoid hash collision.
Nonce uint32 Nonce uint32
// Address signed the transaction.
Sender util.Uint160
// Fee to be burned. // Fee to be burned.
SystemFee int64 SystemFee int64
@ -51,8 +48,8 @@ type Transaction struct {
// Transaction attributes. // Transaction attributes.
Attributes []Attribute Attributes []Attribute
// Transaction cosigners (not include Sender). // Transaction signers list (starts with Sender).
Cosigners []Cosigner Signers []Signer
// The scripts that comes with this transaction. // The scripts that comes with this transaction.
// Scripts exist out of the verification script // Scripts exist out of the verification script
@ -96,7 +93,7 @@ func New(network netmode.Magic, script []byte, gas int64) *Transaction {
Script: script, Script: script,
SystemFee: gas, SystemFee: gas,
Attributes: []Attribute{}, Attributes: []Attribute{},
Cosigners: []Cosigner{}, Signers: []Signer{},
Scripts: []Witness{}, Scripts: []Witness{},
Network: network, Network: network,
} }
@ -131,7 +128,6 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) {
return return
} }
t.Nonce = br.ReadU32LE() t.Nonce = br.ReadU32LE()
t.Sender.DecodeBinary(br)
t.SystemFee = int64(br.ReadU64LE()) t.SystemFee = int64(br.ReadU64LE())
if t.SystemFee < 0 { if t.SystemFee < 0 {
br.Err = errors.New("negative system fee") br.Err = errors.New("negative system fee")
@ -148,22 +144,25 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) {
} }
t.ValidUntilBlock = br.ReadU32LE() t.ValidUntilBlock = br.ReadU32LE()
br.ReadArray(&t.Attributes) br.ReadArray(&t.Signers, MaxAttributes)
if len(t.Signers) == 0 {
br.ReadArray(&t.Cosigners, MaxCosigners) br.Err = errors.New("signers array should contain sender")
for i := 0; i < len(t.Cosigners); i++ { return
if t.Cosigners[i].Scopes == FeeOnly { }
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") br.Err = errors.New("FeeOnly scope can be used only for sender")
return return
} }
for j := i + 1; j < len(t.Cosigners); j++ { for j := i + 1; j < len(t.Signers); j++ {
if t.Cosigners[i].Account.Equals(t.Cosigners[j].Account) { if t.Signers[i].Account.Equals(t.Signers[j].Account) {
br.Err = errors.New("transaction cosigners should be unique") br.Err = errors.New("transaction signers should be unique")
return return
} }
} }
} }
br.ReadArray(&t.Attributes, MaxAttributes-len(t.Signers))
t.Script = br.ReadVarBytes() t.Script = br.ReadVarBytes()
if br.Err == nil && len(t.Script) == 0 { if br.Err == nil && len(t.Script) == 0 {
br.Err = errors.New("no script") br.Err = errors.New("no script")
@ -201,17 +200,11 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
} }
bw.WriteB(byte(t.Version)) bw.WriteB(byte(t.Version))
bw.WriteU32LE(t.Nonce) bw.WriteU32LE(t.Nonce)
t.Sender.EncodeBinary(bw)
bw.WriteU64LE(uint64(t.SystemFee)) bw.WriteU64LE(uint64(t.SystemFee))
bw.WriteU64LE(uint64(t.NetworkFee)) bw.WriteU64LE(uint64(t.NetworkFee))
bw.WriteU32LE(t.ValidUntilBlock) bw.WriteU32LE(t.ValidUntilBlock)
bw.WriteArray(t.Signers)
// Attributes
bw.WriteArray(t.Attributes) bw.WriteArray(t.Attributes)
// Cosigners
bw.WriteArray(t.Cosigners)
bw.WriteVarBytes(t.Script) bw.WriteVarBytes(t.Script)
} }
@ -297,6 +290,15 @@ func (t *Transaction) FeePerByte() int64 {
return t.feePerByte 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 // transactionJSON is a wrapper for Transaction and
// used for correct marhalling of transaction.Data // used for correct marhalling of transaction.Data
type transactionJSON struct { type transactionJSON struct {
@ -309,7 +311,7 @@ type transactionJSON struct {
NetworkFee int64 `json:"netfee,string"` NetworkFee int64 `json:"netfee,string"`
ValidUntilBlock uint32 `json:"validuntilblock"` ValidUntilBlock uint32 `json:"validuntilblock"`
Attributes []Attribute `json:"attributes"` Attributes []Attribute `json:"attributes"`
Cosigners []Cosigner `json:"cosigners"` Signers []Signer `json:"signers"`
Script []byte `json:"script"` Script []byte `json:"script"`
Scripts []Witness `json:"witnesses"` Scripts []Witness `json:"witnesses"`
} }
@ -321,10 +323,10 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
Size: io.GetVarSize(t), Size: io.GetVarSize(t),
Version: t.Version, Version: t.Version,
Nonce: t.Nonce, Nonce: t.Nonce,
Sender: address.Uint160ToString(t.Sender), Sender: address.Uint160ToString(t.Sender()),
ValidUntilBlock: t.ValidUntilBlock, ValidUntilBlock: t.ValidUntilBlock,
Attributes: t.Attributes, Attributes: t.Attributes,
Cosigners: t.Cosigners, Signers: t.Signers,
Script: t.Script, Script: t.Script,
Scripts: t.Scripts, Scripts: t.Scripts,
SystemFee: t.SystemFee, SystemFee: t.SystemFee,
@ -343,15 +345,10 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
t.Nonce = tx.Nonce t.Nonce = tx.Nonce
t.ValidUntilBlock = tx.ValidUntilBlock t.ValidUntilBlock = tx.ValidUntilBlock
t.Attributes = tx.Attributes t.Attributes = tx.Attributes
t.Cosigners = tx.Cosigners t.Signers = tx.Signers
t.Scripts = tx.Scripts t.Scripts = tx.Scripts
t.SystemFee = tx.SystemFee t.SystemFee = tx.SystemFee
t.NetworkFee = tx.NetworkFee 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 t.Script = tx.Script
if t.Hash() != tx.TxID { if t.Hash() != tx.TxID {
return errors.New("txid doesn't match transaction hash") return errors.New("txid doesn't match transaction hash")

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "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/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -64,6 +65,7 @@ func TestDecodeEncodeInvocationTX(t *testing.T) {
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
script := []byte{0x51} script := []byte{0x51}
tx := New(netmode.UnitTestNet, script, 1) 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, int64(1), tx.SystemFee)
assert.Equal(t, script, tx.Script) assert.Equal(t, script, tx.Script)
// Update hash fields to match tx2 that is gonna autoupdate them on decode. // 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} script := []byte{0x51}
tx := New(netmode.UnitTestNet, script, 1) tx := New(netmode.UnitTestNet, script, 1)
tx.NetworkFee = 123 tx.NetworkFee = 123
tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}}
data, err := testserdes.EncodeBinary(tx) data, err := testserdes.EncodeBinary(tx)
require.NoError(t, err) require.NoError(t, err)
@ -106,6 +109,7 @@ func TestDecodingTXWithNoScript(t *testing.T) {
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) { func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
tx := &Transaction{ tx := &Transaction{
Version: 0, Version: 0,
Signers: []Signer{{Account: util.Uint160{1, 2, 3}}},
Script: []byte{1, 2, 3, 4}, Script: []byte{1, 2, 3, 4},
Attributes: []Attribute{}, Attributes: []Attribute{},
Scripts: []Witness{}, Scripts: []Witness{},

View file

@ -7,7 +7,7 @@ import (
"strings" "strings"
) )
// WitnessScope represents set of witness flags for Transaction cosigner. // WitnessScope represents set of witness flags for Transaction signer.
type WitnessScope byte type WitnessScope byte
const ( const (

View file

@ -78,7 +78,12 @@ func deployNativeContracts(magic netmode.Magic) *transaction.Transaction {
script := buf.Bytes() script := buf.Bytes()
tx := transaction.New(magic, script, 0) tx := transaction.New(magic, script, 0)
tx.Nonce = 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{ tx.Scripts = []transaction.Witness{
{ {
InvocationScript: []byte{}, InvocationScript: []byte{},

View file

@ -21,7 +21,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
// have been changed. Consequently, hash of the genesis block has been changed. // have been changed. Consequently, hash of the genesis block has been changed.
// Update expected genesis block hash for better times. // Update expected genesis block hash for better times.
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf" // Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
expect := "94e61af2441145cc251752707a58107850328a48bb095fd175ca2f8513ab5676" expect := "ecaee33262f1bc7c7c28f2b25b54a5d61d50670871f45c0c6fe755a40cbde4a8"
assert.Equal(t, expect, block.Hash().StringLE()) assert.Equal(t, expect, block.Hash().StringLE())
} }

View file

@ -131,7 +131,7 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, token util.Uint1
} }
script := w.Bytes() script := w.Bytes()
result, err := c.InvokeScript(script, []transaction.Cosigner{ result, err := c.InvokeScript(script, []transaction.Signer{
{ {
Account: from, Account: from,
Scopes: transaction.CalledByEntry, 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) return nil, fmt.Errorf("can't add system fee to transaction: %v", err)
} }
tx := transaction.New(c.opts.Network, script, result.GasConsumed) tx := transaction.New(c.opts.Network, script, result.GasConsumed)
tx.Sender = from tx.Signers = []transaction.Signer{
tx.Cosigners = []transaction.Cosigner{
{ {
Account: from, Account: from,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,

View file

@ -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. // 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. // 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)) 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 // InvokeFunction returns the results after calling the smart contract scripthash
// with the given operation and parameters. // with the given operation and parameters.
// NOTE: this is test invoke and will not affect the blockchain. // 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) 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 // 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) var resp = new(result.Invoke)
if cosigners != nil { if signers != nil {
p.Values = append(p.Values, cosigners) p.Values = append(p.Values, signers)
} }
if err := c.performRequest(method, p, resp); err != nil { if err := c.performRequest(method, p, resp); err != nil {
// Retry with old-fashioned hashes (see neo/neo-modules#260). // Retry with old-fashioned hashes (see neo/neo-modules#260).
if cosigners != nil { if signers != nil {
var hashes = make([]util.Uint160, len(cosigners)) var hashes = make([]util.Uint160, len(signers))
for i := range cosigners { for i := range signers {
hashes[i] = cosigners[i].Account hashes[i] = signers[i].Account
} }
p.Values[len(p.Values)-1] = hashes p.Values[len(p.Values)-1] = hashes
err = c.performRequest(method, p, resp) err = c.performRequest(method, p, resp)
@ -419,13 +419,18 @@ func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) {
// SignAndPushInvocationTx signs and pushes given script as an invocation // SignAndPushInvocationTx signs and pushes given script as an invocation
// transaction using given wif to sign it and spending the amount of gas // 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. // 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 txHash util.Uint256
var err error var err error
tx := transaction.New(c.opts.Network, script, sysfee) tx := transaction.New(c.opts.Network, script, sysfee)
tx.SystemFee = sysfee tx.SystemFee = sysfee
tx.Cosigners = cosigners
addr, err := address.StringToUint160(acc.Address)
if err != nil {
return txHash, errors.Wrap(err, "failed to get address")
}
tx.Signers = getSigners(addr, cosigners)
validUntilBlock, err := c.CalculateValidUntilBlock() validUntilBlock, err := c.CalculateValidUntilBlock()
if err != nil { if err != nil {
@ -433,12 +438,6 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys
} }
tx.ValidUntilBlock = validUntilBlock tx.ValidUntilBlock = validUntilBlock
addr, err := address.StringToUint160(acc.Address)
if err != nil {
return txHash, errors.Wrap(err, "failed to get address")
}
tx.Sender = addr
err = c.AddNetworkFee(tx, int64(netfee), acc) err = c.AddNetworkFee(tx, int64(netfee), acc)
if err != nil { if err != nil {
return txHash, errors.Wrapf(err, "failed to add network fee") return txHash, errors.Wrapf(err, "failed to add network fee")
@ -458,6 +457,27 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys
return txHash, nil 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. // ValidateAddress verifies that the address is a correct NEO address.
func (c *Client) ValidateAddress(address string) error { func (c *Client) ValidateAddress(address string) error {
var ( var (
@ -513,7 +533,7 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, acc
tx.NetworkFee += netFee tx.NetworkFee += netFee
size += sizeDelta size += sizeDelta
} }
for _, cosigner := range tx.Cosigners { for _, cosigner := range tx.Signers {
script := acc.Contract.Script script := acc.Contract.Script
if !cosigner.Account.Equals(hash.Hash160(acc.Contract.Script)) { if !cosigner.Account.Equals(hash.Hash160(acc.Contract.Script)) {
contract, err := c.GetContractState(cosigner.Account) contract, err := c.GetContractState(cosigner.Account)

View file

@ -41,17 +41,17 @@ type rpcClientTestCase struct {
check func(t *testing.T, c *Client, result interface{}) 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. // getResultBlock1 returns data for block number 1 which is used by several tests.
func getResultBlock1() *result.Block { func getResultBlock1() *result.Block {
@ -64,14 +64,14 @@ func getResultBlock1() *result.Block {
if err != nil { if err != nil {
panic(err) panic(err)
} }
b2Hash, err := util.Uint256DecodeStringLE("45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d") b2Hash, err := util.Uint256DecodeStringLE("003abea54aa3c5edba7e33fb7ca96452cb65ff8cd36ce1cdfd412a6c4d3ea38a")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return &result.Block{ return &result.Block{
Block: *b, Block: *b,
BlockMetadata: result.BlockMetadata{ BlockMetadata: result.BlockMetadata{
Size: 1681, Size: 1641,
NextBlockHash: &b2Hash, NextBlockHash: &b2Hash,
Confirmations: 6, Confirmations: 6,
}, },
@ -635,7 +635,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
Type: smartcontract.Hash160Type, Type: smartcontract.Hash160Type,
Value: hash, Value: hash,
}, },
}, []transaction.Cosigner{{ }, []transaction.Signer{{
Account: util.Uint160{1, 2, 3}, Account: util.Uint160{1, 2, 3},
}}) }})
}, },
@ -662,7 +662,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
if err != nil { if err != nil {
panic(err) panic(err)
} }
return c.InvokeScript(script, []transaction.Cosigner{{ return c.InvokeScript(script, []transaction.Signer{{
Account: util.Uint160{1, 2, 3}, Account: util.Uint160{1, 2, 3},
}}) }})
}, },

View file

@ -255,12 +255,12 @@ func (c *WSClient) SubscribeForNewBlocks(primary *int) (string, error) {
} }
// SubscribeForNewTransactions adds subscription for new transaction events to // 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. // 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") params := request.NewRawParams("transaction_added")
if sender != nil || cosigner != nil { if sender != nil || signer != nil {
params.Values = append(params.Values, request.TxFilter{Sender: sender, Cosigner: cosigner}) params.Values = append(params.Values, request.TxFilter{Sender: sender, Signer: signer})
} }
return c.performSubscription(params) return c.performSubscription(params)
} }

View file

@ -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":"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":"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":"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":[]}`, `{"jsonrpc":"2.0","method":"event_missed","params":[]}`,
} }
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 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) filt, ok := param.Value.(request.TxFilter)
require.Equal(t, true, ok) require.Equal(t, true, ok)
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender) 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) { func(t *testing.T, wsc *WSClient) {
cosigner := util.Uint160{0, 42} signer := util.Uint160{0, 42}
_, err := wsc.SubscribeForNewTransactions(nil, &cosigner) _, err := wsc.SubscribeForNewTransactions(nil, &signer)
require.NoError(t, err) require.NoError(t, err)
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
@ -220,14 +220,14 @@ func TestWSFilteredSubscriptions(t *testing.T) {
filt, ok := param.Value.(request.TxFilter) filt, ok := param.Value.(request.TxFilter)
require.Equal(t, true, ok) require.Equal(t, true, ok)
require.Nil(t, filt.Sender) 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) { func(t *testing.T, wsc *WSClient) {
sender := util.Uint160{1, 2, 3, 4, 5} sender := util.Uint160{1, 2, 3, 4, 5}
cosigner := util.Uint160{0, 42} signer := util.Uint160{0, 42}
_, err := wsc.SubscribeForNewTransactions(&sender, &cosigner) _, err := wsc.SubscribeForNewTransactions(&sender, &signer)
require.NoError(t, err) require.NoError(t, err)
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
@ -237,7 +237,7 @@ func TestWSFilteredSubscriptions(t *testing.T) {
filt, ok := param.Value.(request.TxFilter) filt, ok := param.Value.(request.TxFilter)
require.Equal(t, true, ok) require.Equal(t, true, ok)
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender) 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", {"notifications",

View file

@ -37,10 +37,10 @@ type (
Primary int `json:"primary"` Primary int `json:"primary"`
} }
// TxFilter is a wrapper structure for transaction event filter. It // 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 { TxFilter struct {
Sender *util.Uint160 `json:"sender,omitempty"` Sender *util.Uint160 `json:"sender,omitempty"`
Cosigner *util.Uint160 `json:"cosigner,omitempty"` Signer *util.Uint160 `json:"signer,omitempty"`
} }
// NotificationFilter is a wrapper structure representing filter used for // NotificationFilter is a wrapper structure representing filter used for
// notifications generated during transaction execution. Notifications can // notifications generated during transaction execution. Notifications can
@ -67,7 +67,7 @@ const (
TxFilterT TxFilterT
NotificationFilterT NotificationFilterT
ExecutionFilterT ExecutionFilterT
Cosigner Signer
) )
var errMissingParameter = errors.New("parameter is missing") var errMissingParameter = errors.New("parameter is missing")
@ -207,24 +207,24 @@ func (p *Param) GetBytesBase64() ([]byte, error) {
return base64.StdEncoding.DecodeString(s) return base64.StdEncoding.DecodeString(s)
} }
// GetCosigner returns transaction.Cosigner value of the parameter. // GetSigner returns transaction.Signer value of the parameter.
func (p Param) GetCosigner() (transaction.Cosigner, error) { func (p Param) GetSigner() (transaction.Signer, error) {
c, ok := p.Value.(transaction.Cosigner) c, ok := p.Value.(transaction.Signer)
if !ok { if !ok {
return transaction.Cosigner{}, errors.New("not a cosigner") return transaction.Signer{}, errors.New("not a signer")
} }
return c, nil return c, nil
} }
// GetCosigners returns a slice of transaction.Cosigner with global scope from // GetSigners returns a slice of transaction.Signer with global scope from
// array of Uint160 or array of serialized transaction.Cosigner stored in the // array of Uint160 or array of serialized transaction.Signer stored in the
// parameter. // parameter.
func (p Param) GetCosigners() ([]transaction.Cosigner, error) { func (p Param) GetSigners() ([]transaction.Signer, error) {
hashes, err := p.GetArray() hashes, err := p.GetArray()
if err != nil { if err != nil {
return nil, err return nil, err
} }
cosigners := make([]transaction.Cosigner, len(hashes)) signers := make([]transaction.Signer, len(hashes))
// try to extract hashes first // try to extract hashes first
for i, h := range hashes { for i, h := range hashes {
var u util.Uint160 var u util.Uint160
@ -232,20 +232,20 @@ func (p Param) GetCosigners() ([]transaction.Cosigner, error) {
if err != nil { if err != nil {
break break
} }
cosigners[i] = transaction.Cosigner{ signers[i] = transaction.Signer{
Account: u, Account: u,
Scopes: transaction.Global, Scopes: transaction.Global,
} }
} }
if err != nil { if err != nil {
for i, h := range hashes { for i, h := range hashes {
cosigners[i], err = h.GetCosigner() signers[i], err = h.GetSigner()
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
} }
return cosigners, nil return signers, nil
} }
// UnmarshalJSON implements json.Unmarshaler interface. // UnmarshalJSON implements json.Unmarshaler interface.
@ -261,7 +261,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
{TxFilterT, &TxFilter{}}, {TxFilterT, &TxFilter{}},
{NotificationFilterT, &NotificationFilter{}}, {NotificationFilterT, &NotificationFilter{}},
{ExecutionFilterT, &ExecutionFilter{}}, {ExecutionFilterT, &ExecutionFilter{}},
{Cosigner, &transaction.Cosigner{}}, {Signer, &transaction.Signer{}},
{ArrayT, &[]Param{}}, {ArrayT, &[]Param{}},
} }
@ -296,7 +296,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
} else { } else {
continue continue
} }
case *transaction.Cosigner: case *transaction.Signer:
p.Value = *val p.Value = *val
case *[]Param: case *[]Param:
p.Value = *val p.Value = *val

View file

@ -18,8 +18,8 @@ func TestParam_UnmarshalJSON(t *testing.T) {
msg := `["str1", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}], msg := `["str1", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}],
{"primary": 1}, {"primary": 1},
{"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, {"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
{"cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, {"signer": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
{"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554", "cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, {"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554", "signer": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
{"contract": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"}, {"contract": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
{"state": "HALT"}, {"state": "HALT"},
{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}, {"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"},
@ -78,11 +78,11 @@ func TestParam_UnmarshalJSON(t *testing.T) {
}, },
{ {
Type: TxFilterT, Type: TxFilterT,
Value: TxFilter{Cosigner: &contr}, Value: TxFilter{Signer: &contr},
}, },
{ {
Type: TxFilterT, Type: TxFilterT,
Value: TxFilter{Sender: &contr, Cosigner: &contr}, Value: TxFilter{Sender: &contr, Signer: &contr},
}, },
{ {
Type: NotificationFilterT, Type: NotificationFilterT,
@ -93,8 +93,8 @@ func TestParam_UnmarshalJSON(t *testing.T) {
Value: ExecutionFilter{State: "HALT"}, Value: ExecutionFilter{State: "HALT"},
}, },
{ {
Type: Cosigner, Type: Signer,
Value: transaction.Cosigner{ Value: transaction.Signer{
Account: accountHash, Account: accountHash,
Scopes: transaction.FeeOnly, Scopes: transaction.FeeOnly,
}, },
@ -103,8 +103,8 @@ func TestParam_UnmarshalJSON(t *testing.T) {
Type: ArrayT, Type: ArrayT,
Value: []Param{ Value: []Param{
{ {
Type: Cosigner, Type: Signer,
Value: transaction.Cosigner{ Value: transaction.Signer{
Account: accountHash, Account: accountHash,
Scopes: transaction.Global, Scopes: transaction.Global,
}, },
@ -280,22 +280,22 @@ func TestParamGetBytesBase64(t *testing.T) {
require.NotNil(t, err) require.NotNil(t, err)
} }
func TestParamGetCosigner(t *testing.T) { func TestParamGetSigner(t *testing.T) {
c := transaction.Cosigner{ c := transaction.Signer{
Account: util.Uint160{1, 2, 3, 4}, Account: util.Uint160{1, 2, 3, 4},
Scopes: transaction.Global, Scopes: transaction.Global,
} }
p := Param{Type: Cosigner, Value: c} p := Param{Type: Signer, Value: c}
actual, err := p.GetCosigner() actual, err := p.GetSigner()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c, actual) require.Equal(t, c, actual)
p = Param{Type: Cosigner, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`} p = Param{Type: Signer, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`}
_, err = p.GetCosigner() _, err = p.GetSigner()
require.Error(t, err) require.Error(t, err)
} }
func TestParamGetCosigners(t *testing.T) { func TestParamGetSigners(t *testing.T) {
u1 := util.Uint160{1, 2, 3, 4} u1 := util.Uint160{1, 2, 3, 4}
u2 := util.Uint160{5, 6, 7, 8} u2 := util.Uint160{5, 6, 7, 8}
t.Run("from hashes", func(t *testing.T) { 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: u1.StringLE()},
{Type: StringT, Value: u2.StringLE()}, {Type: StringT, Value: u2.StringLE()},
}} }}
actual, err := p.GetCosigners() actual, err := p.GetSigners()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(actual)) require.Equal(t, 2, len(actual))
require.True(t, u1.Equals(actual[0].Account)) require.True(t, u1.Equals(actual[0].Account))
require.True(t, u2.Equals(actual[1].Account)) require.True(t, u2.Equals(actual[1].Account))
}) })
t.Run("from cosigners", func(t *testing.T) { t.Run("from signers", func(t *testing.T) {
c1 := transaction.Cosigner{ c1 := transaction.Signer{
Account: u1, Account: u1,
Scopes: transaction.Global, Scopes: transaction.Global,
} }
c2 := transaction.Cosigner{ c2 := transaction.Signer{
Account: u2, Account: u2,
Scopes: transaction.CustomContracts, Scopes: transaction.CustomContracts,
AllowedContracts: []util.Uint160{ AllowedContracts: []util.Uint160{
@ -324,10 +324,10 @@ func TestParamGetCosigners(t *testing.T) {
}, },
} }
p := Param{ArrayT, []Param{ p := Param{ArrayT, []Param{
{Type: Cosigner, Value: c1}, {Type: Signer, Value: c1},
{Type: Cosigner, Value: c2}, {Type: Signer, Value: c2},
}} }}
actual, err := p.GetCosigners() actual, err := p.GetSigners()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(actual)) require.Equal(t, 2, len(actual))
require.Equal(t, c1, actual[0]) require.Equal(t, c1, actual[0])
@ -339,7 +339,7 @@ func TestParamGetCosigners(t *testing.T) {
{Type: StringT, Value: u1.StringLE()}, {Type: StringT, Value: u1.StringLE()},
{Type: StringT, Value: "bla"}, {Type: StringT, Value: "bla"},
}} }}
_, err := p.GetCosigners() _, err := p.GetSigners()
require.Error(t, err) require.Error(t, err)
}) })
} }

View file

@ -843,13 +843,16 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
tx := &transaction.Transaction{} tx := &transaction.Transaction{}
checkWitnessHashesIndex := len(reqParams) checkWitnessHashesIndex := len(reqParams)
if checkWitnessHashesIndex > 3 { if checkWitnessHashesIndex > 3 {
cosigners, err := reqParams[3].GetCosigners() signers, err := reqParams[3].GetSigners()
if err != nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
tx.Cosigners = cosigners tx.Signers = signers
checkWitnessHashesIndex-- checkWitnessHashesIndex--
} }
if len(tx.Signers) == 0 {
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.FeeOnly}}
}
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:checkWitnessHashesIndex]) script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:checkWitnessHashesIndex])
if err != nil { if err != nil {
return nil, response.NewInternalServerError("can't create invocation script", err) 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{} tx := &transaction.Transaction{}
if len(reqParams) > 1 { if len(reqParams) > 1 {
cosigners, err := reqParams[1].GetCosigners() signers, err := reqParams[1].GetSigners()
if err != nil { if err != nil {
return nil, response.ErrInvalidParams 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 tx.Script = script
return s.runScriptInVM(script, tx), nil return s.runScriptInVM(script, tx), nil

View file

@ -52,7 +52,7 @@ type rpcTestCase struct {
} }
const testContractHash = "36c3b0c85d98607db00b711885ec3e411d9b1672" const testContractHash = "36c3b0c85d98607db00b711885ec3e411d9b1672"
const deploymentTxHash = "ef4209bc06e1d8412995c645a8497d3e2c9a05ca52236de94297c6db9c3e94d0" const deploymentTxHash = "60a1fc8ceb7948d9933aec0cedd148441568575c40af7e0985cc366ed153d57e"
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
@ -589,12 +589,12 @@ var rpcTestCases = map[string][]rpcTestCase{
"sendrawtransaction": { "sendrawtransaction": {
{ {
name: "positive", name: "positive",
params: `["000a000000aa8acf859d4fe402b34e673f2156821796a488eb80969800000000009269130000000000b00400000001aa8acf859d4fe402b34e673f2156821796a488eb015d0300e87648170000000c1478ba4c24009fe510e136c9995a2e05215e1be4dc0c14aa8acf859d4fe402b34e673f2156821796a488eb13c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c4040719393aa590d962cb5a48e16360ac75a6c358c9699e9f1a853afede4d601b6783e28f5ec74542aaf59519e76830ba9d267656db324461fdb08d1d51521e103290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"]`, params: `["000a0000008096980000000000721b130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005d0300e87648170000000c1478ba4c24009fe510e136c9995a2e05215e1be4dc0c14aa8acf859d4fe402b34e673f2156821796a488eb13c00c087472616e736665720c1425059ecb4878d3a875f91c51ceded330d4575fde41627d5b523801420c40b99503c74bb1861b0b45060501dd090224f6c404aca8c02ccba3243c9b9691c1ef9e6b824d731f8fab27c56ba75609d32d2d176e97f56d9e3780610c83ebd41a290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"]`,
result: func(e *executor) interface{} { return &result.RelayResult{} }, result: func(e *executor) interface{} { return &result.RelayResult{} },
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.RelayResult) res, ok := inv.(*result.RelayResult)
require.True(t, ok) require.True(t, ok)
expectedHash, err := util.Uint256DecodeStringLE("72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875") expectedHash, err := util.Uint256DecodeStringLE("8b6e610a2205914411b26c4380594fa9a1e16961ff5896ed3b16831a151c6dd0")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expectedHash, res.Hash) 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 := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0)
tx.Nonce = height + 1 tx.Nonce = height + 1
tx.ValidUntilBlock = height + 10 tx.ValidUntilBlock = height + 10
tx.Sender = acc0.PrivateKey().GetScriptHash() tx.Signers = []transaction.Signer{{Account: acc0.PrivateKey().GetScriptHash()}}
addNetworkFee(tx) addNetworkFee(tx)
require.NoError(t, acc0.SignTx(tx)) require.NoError(t, acc0.SignTx(tx))
return tx return tx
@ -872,6 +872,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
} }
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) 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{})) assert.NoError(t, mp.Add(tx, &FeerStub{}))
expected = append(expected, tx.Hash()) expected = append(expected, tx.Hash())
} }
@ -1000,7 +1001,7 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) {
}, },
{ {
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Amount: "915.79002700", Amount: "915.78962700",
LastUpdated: 6, LastUpdated: 6,
}}, }},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), 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) b, err := e.chain.GetBlock(h)
require.NoError(t, err) require.NoError(t, err)
for j := range b.Transactions { 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 amount := b.Transactions[j].SystemFee + b.Transactions[j].NetworkFee
expected.Sent = append(expected.Sent, result.NEP5Transfer{ expected.Sent = append(expected.Sent, result.NEP5Transfer{
Timestamp: b.Timestamp, Timestamp: b.Timestamp,

View file

@ -58,18 +58,18 @@ func (f *feed) Matches(r *response.Notification) bool {
case response.TransactionEventID: case response.TransactionEventID:
filt := f.filter.(request.TxFilter) filt := f.filter.(request.TxFilter)
tx := r.Payload[0].(*transaction.Transaction) tx := r.Payload[0].(*transaction.Transaction)
senderOK := filt.Sender == nil || tx.Sender.Equals(*filt.Sender) senderOK := filt.Sender == nil || tx.Sender().Equals(*filt.Sender)
cosignerOK := true signerOK := true
if filt.Cosigner != nil { if filt.Signer != nil {
cosignerOK = false signerOK = false
for i := range tx.Cosigners { for i := range tx.Signers {
if tx.Cosigners[i].Account.Equals(*filt.Cosigner) { if tx.Signers[i].Account.Equals(*filt.Signer) {
cosignerOK = true signerOK = true
break break
} }
} }
} }
return senderOK && cosignerOK return senderOK && signerOK
case response.NotificationEventID: case response.NotificationEventID:
filt := f.filter.(request.NotificationFilter) filt := f.filter.(request.NotificationFilter)
notification := r.Payload[0].(result.NotificationEvent) notification := r.Payload[0].(result.NotificationEvent)

View file

@ -147,28 +147,28 @@ func TestFilteredSubscriptions(t *testing.T) {
require.Equal(t, address.Uint160ToString(goodSender), sender) require.Equal(t, address.Uint160ToString(goodSender), sender)
}, },
}, },
"tx matching cosigner": { "tx matching signer": {
params: `["transaction_added", {"cosigner":"` + goodSender.StringLE() + `"}]`, params: `["transaction_added", {"signer":"` + goodSender.StringLE() + `"}]`,
check: func(t *testing.T, resp *response.Notification) { check: func(t *testing.T, resp *response.Notification) {
rmap := resp.Payload[0].(map[string]interface{}) rmap := resp.Payload[0].(map[string]interface{})
require.Equal(t, response.TransactionEventID, resp.Event) require.Equal(t, response.TransactionEventID, resp.Event)
cosigners := rmap["cosigners"].([]interface{}) signers := rmap["signers"].([]interface{})
cosigner0 := cosigners[0].(map[string]interface{}) signer0 := signers[0].(map[string]interface{})
cosigner0acc := cosigner0["account"].(string) signer0acc := signer0["account"].(string)
require.Equal(t, "0x"+goodSender.StringLE(), cosigner0acc) require.Equal(t, "0x"+goodSender.StringLE(), signer0acc)
}, },
}, },
"tx matching sender and cosigner": { "tx matching sender and signer": {
params: `["transaction_added", {"sender":"` + goodSender.StringLE() + `", "cosigner":"` + goodSender.StringLE() + `"}]`, params: `["transaction_added", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `"}]`,
check: func(t *testing.T, resp *response.Notification) { check: func(t *testing.T, resp *response.Notification) {
rmap := resp.Payload[0].(map[string]interface{}) rmap := resp.Payload[0].(map[string]interface{})
require.Equal(t, response.TransactionEventID, resp.Event) require.Equal(t, response.TransactionEventID, resp.Event)
sender := rmap["sender"].(string) sender := rmap["sender"].(string)
require.Equal(t, address.Uint160ToString(goodSender), sender) require.Equal(t, address.Uint160ToString(goodSender), sender)
cosigners := rmap["cosigners"].([]interface{}) signers := rmap["signers"].([]interface{})
cosigner0 := cosigners[0].(map[string]interface{}) signer0 := signers[0].(map[string]interface{})
cosigner0acc := cosigner0["account"].(string) signer0acc := signer0["account"].(string)
require.Equal(t, "0x"+goodSender.StringLE(), cosigner0acc) require.Equal(t, "0x"+goodSender.StringLE(), signer0acc)
}, },
}, },
"notification matching": { "notification matching": {

Binary file not shown.

View file

@ -169,6 +169,7 @@ func getContractTx() *transaction.Transaction {
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0) tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
tx.Attributes = make([]transaction.Attribute, 0) tx.Attributes = make([]transaction.Attribute, 0)
tx.Scripts = make([]transaction.Witness, 0) tx.Scripts = make([]transaction.Witness, 0)
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
tx.Hash() tx.Hash()
return tx return tx
} }