From fb18eda5154292ad0fbea1c401d4d0e3afdca327 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 12:16:25 +0300 Subject: [PATCH 01/18] cli/server: update toNeoStorageKey key conversion for Neo 3 Follow #1037 changes with contract IDs and update padding, the number of value bytes is being stored now as 17th byte instead of number of zeroes. --- cli/server/dump.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/cli/server/dump.go b/cli/server/dump.go index ead0b3b33..233684985 100644 --- a/cli/server/dump.go +++ b/cli/server/dump.go @@ -9,7 +9,6 @@ import ( "path/filepath" "github.com/nspcc-dev/neo-go/pkg/core/storage" - "github.com/nspcc-dev/neo-go/pkg/util" ) type dump []blockDump @@ -26,26 +25,24 @@ type storageOp struct { Value string `json:"value,omitempty"` } -// NEO has some differences of key storing. -// out format: script hash in LE + key -// neo format: script hash in BE + byte(0) + key with 0 between every 16 bytes, padded to len 16. +// NEO has some differences of key storing (that should go away post-preview2). +// ours format: contract's ID (uint32le) + key +// theirs format: contract's ID (uint32le) + key with 16 between every 16 bytes, padded to len 16. func toNeoStorageKey(key []byte) []byte { - if len(key) < util.Uint160Size { + if len(key) < 4 { panic("invalid key in storage") } - var nkey []byte - for i := util.Uint160Size - 1; i >= 0; i-- { - nkey = append(nkey, key[i]) - } - - key = key[util.Uint160Size:] + // Prefix is a contract's ID in LE. + nkey := make([]byte, 4, len(key)) + copy(nkey, key[:4]) + key = key[4:] index := 0 remain := len(key) for remain >= 16 { nkey = append(nkey, key[index:index+16]...) - nkey = append(nkey, 0) + nkey = append(nkey, 16) index += 16 remain -= 16 } @@ -59,7 +56,7 @@ func toNeoStorageKey(key []byte) []byte { nkey = append(nkey, 0) } - nkey = append(nkey, byte(padding)) + nkey = append(nkey, byte(remain)) return nkey } From 66df805f3b3df6ef7f0454cc03836fd88595e07d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 12:25:47 +0300 Subject: [PATCH 02/18] cli/server: update value conversion for Neo 3 Version is no longer being stored for the value, so there is no need for '00' byte. --- cli/server/dump.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/server/dump.go b/cli/server/dump.go index 233684985..e6084a9ee 100644 --- a/cli/server/dump.go +++ b/cli/server/dump.go @@ -81,7 +81,7 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump { ops = append(ops, storageOp{ State: op, Key: hex.EncodeToString(key), - Value: "00" + hex.EncodeToString(batch.Put[i].Value), + Value: hex.EncodeToString(batch.Put[i].Value), }) } From 2f8e7e4d33d0c38cbd1310136f7a663942c3f640 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 13:26:39 +0300 Subject: [PATCH 03/18] native: fix getvalidators to match C# implementation ValidatorsCount is not initialized at block 0 with C# node (the first voter initializes it) and until that initialization happens the standby validators list is being returned as is without sorting. Fixes state mismatch for the key ffffffff0e00000000000000000000000000000001 in the first blocks. It also affects tests as now the first validator is different and it receives the network fees. --- pkg/consensus/consensus_test.go | 7 +++---- pkg/core/helper_test.go | 2 +- pkg/core/native/native_neo.go | 36 ++++++++++++++++----------------- pkg/rpc/server/server_test.go | 15 +++++++++++++- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 6ad040cc5..196bdd5cf 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -213,7 +213,7 @@ func newTestService(t *testing.T) *service { } func getTestValidator(i int) (*privateKey, *publicKey) { - key := testchain.PrivateKey(i) + key := testchain.PrivateKeyByID(i) return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()} } @@ -241,9 +241,8 @@ func signTx(t *testing.T, feePerByte util.Fixed8, txs ...*transaction.Transactio validators := make([]*keys.PublicKey, 4) privNetKeys := make([]*keys.PrivateKey, 4) for i := 0; i < 4; i++ { - privateKey, publicKey := getTestValidator(i) - validators[i] = publicKey.PublicKey - privNetKeys[i] = privateKey.PrivateKey + privNetKeys[i] = testchain.PrivateKey(i) + validators[i] = privNetKeys[i].PublicKey() } rawScript, err := smartcontract.CreateMultiSigRedeemScript(3, validators) require.NoError(t, err) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index f7b840cc5..c27c1ec10 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -210,7 +210,7 @@ func TestCreateBasicChain(t *testing.T) { t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE()) t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE()) - require.Equal(t, util.Fixed8FromInt64(1000), bc.GetUtilityTokenBalance(priv0ScriptHash)) + require.True(t, util.Fixed8FromInt64(1000).CompareTo(bc.GetUtilityTokenBalance(priv0ScriptHash)) <= 0) // info for getblockheader rpc tests t.Logf("header hash: %s", b.Hash().StringLE()) buf := io.NewBufBinWriter() diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index e09481bc1..7a9c7513c 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -112,8 +112,6 @@ func NewNEO() *NEO { // Initialize initializes NEO contract. func (n *NEO) Initialize(ic *interop.Context) error { - var si state.StorageItem - if err := n.nep5TokenNative.Initialize(ic); err != nil { return err } @@ -122,11 +120,6 @@ func (n *NEO) Initialize(ic *interop.Context) error { return errors.New("already initialized") } - vc := new(ValidatorsCount) - si.Value = vc.Bytes() - if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, &si); err != nil { - return err - } h, vs, err := getStandbyValidatorsHash(ic) if err != nil { return err @@ -284,13 +277,20 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public newPubs = append(newPubs, pub) } if lp, lv := len(newPubs), len(acc.Votes); lp != lv { - si := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) + var si *state.StorageItem + var vc *ValidatorsCount + var err error + + si = ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { - return errors.New("validators count uninitialized") - } - vc, err := ValidatorsCountFromBytes(si.Value) - if err != nil { - return err + // The first voter. + si = new(state.StorageItem) + vc = new(ValidatorsCount) + } else { + vc, err = ValidatorsCountFromBytes(si.Value) + if err != nil { + return err + } } if lv > 0 { vc[lv-1].Sub(&vc[lv-1], &acc.Balance) @@ -384,9 +384,13 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Ite // GetValidatorsInternal returns a list of current validators. func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { + standByValidators, err := bc.GetStandByValidators() + if err != nil { + return nil, err + } si := d.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { - return nil, errors.New("validators count uninitialized") + return standByValidators, nil } validatorsCount, err := ValidatorsCountFromBytes(si.Value) if err != nil { @@ -407,10 +411,6 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke }) count := validatorsCount.GetWeightedAverage() - standByValidators, err := bc.GetStandByValidators() - if err != nil { - return nil, err - } if count < len(standByValidators) { count = len(standByValidators) } diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 14729e5ed..73dd0f226 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -147,7 +147,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { Asset: e.chain.UtilityTokenHash(), - Amount: "923.96937740", + Amount: "924.01732700", LastUpdated: 6, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), @@ -259,6 +259,7 @@ var rpcTestCases = map[string][]rpcTestCase{ // take burned gas into account u := testchain.PrivateKeyByID(0).GetScriptHash() for i := 0; i <= int(e.chain.BlockHeight()); i++ { + var netFee util.Fixed8 h := e.chain.GetHeaderHash(i) b, err := e.chain.GetBlock(h) require.NoError(t, err) @@ -274,7 +275,19 @@ var rpcTestCases = map[string][]rpcTestCase{ TxHash: b.Hash(), }) } + netFee += b.Transactions[j].NetworkFee } + if i > 0 { + expected.Received = append(expected.Received, result.NEP5Transfer{ + Timestamp: b.Timestamp, + Asset: e.chain.UtilityTokenHash(), + Address: "", // minted from network fees. + Amount: amountToString(int64(netFee), 8), + Index: b.Index, + TxHash: b.Hash(), + }) + } + } require.Equal(t, expected.Address, res.Address) require.ElementsMatch(t, expected.Sent, res.Sent) From c124d2bcdfc128752ef0f6442a54069ecfe98bce Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 17:31:09 +0300 Subject: [PATCH 04/18] core: fix gas generation coefficients to match Neo 3 Follow neo-project/neo#911. Fixes state differences at block 4528 of preview2 testnet compared to C# node. --- pkg/core/blockchain.go | 2 +- pkg/rpc/server/server_test.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 80f06efb9..5824a0649 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -54,7 +54,7 @@ var ( ErrInvalidBlockIndex error = errors.New("invalid block index") ) var ( - genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + genAmount = []int{6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} decrementInterval = 2000000 persistInterval = 1 * time.Second ) diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 73dd0f226..f4b38862c 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -147,7 +147,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { Asset: e.chain.UtilityTokenHash(), - Amount: "924.01732700", + Amount: "918.01738700", LastUpdated: 6, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), @@ -229,7 +229,7 @@ var rpcTestCases = map[string][]rpcTestCase{ Timestamp: blockSendNEO.Timestamp, Asset: e.chain.UtilityTokenHash(), Address: "", // Minted GAS. - Amount: "23.99976000", + Amount: "17.99982000", Index: 4, NotifyIndex: 0, TxHash: txSendNEOHash, @@ -565,8 +565,7 @@ var rpcTestCases = map[string][]rpcTestCase{ check: func(t *testing.T, e *executor, resp interface{}) { s, ok := resp.(*string) require.True(t, ok) - // Incorrect, to be fixed later. - assert.Equal(t, "48000", *s) + assert.Equal(t, "36000", *s) }, }, }, From cd2dca02594453e90b12e273cea38216fc7bc663 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 17:38:19 +0300 Subject: [PATCH 05/18] state: fix the way NEO balance is being serialized Actually, our format is way better, but for preview2 compatibility we need to use this one. --- pkg/core/state/native_state.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index bb1d94ebf..bfb9fd10e 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -104,28 +104,20 @@ func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) { if r.Err != nil { return } - s.fromStackItem(si) + r.Err = s.fromStackItem(si) } func (s *NEOBalanceState) toStackItem() stackitem.Item { result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct) result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight)))) - votes := make([]stackitem.Item, len(s.Votes)) - for i, v := range s.Votes { - votes[i] = stackitem.NewByteArray(v.Bytes()) - } - result.Append(stackitem.NewArray(votes)) + result.Append(stackitem.NewByteArray(s.Votes.Bytes())) return result } -func (s *NEOBalanceState) fromStackItem(item stackitem.Item) { +func (s *NEOBalanceState) fromStackItem(item stackitem.Item) error { structItem := item.Value().([]stackitem.Item) s.Balance = *structItem[0].Value().(*big.Int) s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64()) - votes := structItem[2].Value().([]stackitem.Item) - s.Votes = make([]*keys.PublicKey, len(votes)) - for i, v := range votes { - s.Votes[i] = new(keys.PublicKey) - s.Votes[i].DecodeBytes(v.Value().([]byte)) - } + s.Votes = make(keys.PublicKeys, 0) + return s.Votes.DecodeBytes(structItem[2].Value().([]byte)) } From e5f05790d5a329874a0b7cdfac97b3c724081964 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 18:15:55 +0300 Subject: [PATCH 06/18] core: cache standby validators in the Blockchain They never change, so it makes no sense parsing the keys over and over again. It also simplifies the interface a little. --- pkg/core/blockchain.go | 13 +++++++++++-- pkg/core/blockchainer/blockchainer.go | 2 +- pkg/core/helper_test.go | 7 ++----- pkg/core/native/native_gas.go | 5 +---- pkg/core/native/native_neo.go | 5 +---- pkg/core/util.go | 4 ++-- pkg/core/util_test.go | 2 +- pkg/network/helper_test.go | 2 +- pkg/rpc/server/server_test.go | 3 +-- 9 files changed, 21 insertions(+), 22 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 5824a0649..56123da5e 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -117,6 +117,8 @@ type Blockchain struct { // cache for block verification keys. keyCache map[util.Uint160]map[string]*keys.PublicKey + sbValidators keys.PublicKeys + log *zap.Logger lastBatch *storage.MemBatch @@ -152,6 +154,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L cfg.MemPoolSize = defaultMemPoolSize log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize)) } + validators, err := validatorsFromConfig(cfg) + if err != nil { + return nil, err + } bc := &Blockchain{ config: cfg, dao: dao.NewSimple(s, cfg.Magic), @@ -161,6 +167,7 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L runToExitCh: make(chan struct{}), memPool: mempool.NewMemPool(cfg.MemPoolSize), keyCache: make(map[util.Uint160]map[string]*keys.PublicKey), + sbValidators: validators, log: log, events: make(chan bcEvent), subCh: make(chan interface{}), @@ -1235,8 +1242,10 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error { } //GetStandByValidators returns validators from the configuration. -func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) { - return getValidators(bc.config) +func (bc *Blockchain) GetStandByValidators() keys.PublicKeys { + res := make(keys.PublicKeys, len(bc.sbValidators)) + copy(res, bc.sbValidators) + return res } // GetValidators returns next block validators. diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index bbe30a972..ef667c51e 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -37,7 +37,7 @@ type Blockchainer interface { GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5Balances(util.Uint160) *state.NEP5Balances GetValidators() ([]*keys.PublicKey, error) - GetStandByValidators() (keys.PublicKeys, error) + GetStandByValidators() keys.PublicKeys GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetStorageItem(id int32, key []byte) *state.StorageItem GetStorageItems(id int32) (map[string]*state.StorageItem, error) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index c27c1ec10..fb16b2f43 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -48,7 +48,7 @@ func (bc *Blockchain) newBlock(txs ...*transaction.Transaction) *block.Block { } func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, txs ...*transaction.Transaction) *block.Block { - validators, _ := getValidators(cfg) + validators, _ := validatorsFromConfig(cfg) vlen := len(validators) valScript, _ := smartcontract.CreateMultiSigRedeemScript( vlen-(vlen-1)/3, @@ -399,10 +399,7 @@ func addCosigners(txs ...*transaction.Transaction) { } func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { - validators, err := getValidators(bc.config) - if err != nil { - return errors.Wrap(err, "fail to sign tx") - } + validators := bc.GetStandByValidators() rawScript, err := smartcontract.CreateMultiSigRedeemScript(len(bc.config.StandbyValidators)/2+1, validators) if err != nil { return errors.Wrap(err, "fail to sign tx") diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 2e1e57ada..d5eef961c 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -101,10 +101,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error { } func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) { - vs, err := ic.Chain.GetStandByValidators() - if err != nil { - return util.Uint160{}, nil, err - } + vs := ic.Chain.GetStandByValidators() s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs) if err != nil { return util.Uint160{}, nil, err diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 7a9c7513c..eed219825 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -384,10 +384,7 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Ite // GetValidatorsInternal returns a list of current validators. func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { - standByValidators, err := bc.GetStandByValidators() - if err != nil { - return nil, err - } + standByValidators := bc.GetStandByValidators() si := d.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { return standByValidators, nil diff --git a/pkg/core/util.go b/pkg/core/util.go index de081004f..339422900 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -34,7 +34,7 @@ var ( // createGenesisBlock creates a genesis block based on the given configuration. func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) { - validators, err := getValidators(cfg) + validators, err := validatorsFromConfig(cfg) if err != nil { return nil, err } @@ -91,7 +91,7 @@ func deployNativeContracts(magic netmode.Magic) *transaction.Transaction { return tx } -func getValidators(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) { +func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) { validators := make([]*keys.PublicKey, len(cfg.StandbyValidators)) for i, pubKeyStr := range cfg.StandbyValidators { pubKey, err := keys.NewPublicKeyFromString(pubKeyStr) diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 9d5002742..de438e42c 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -34,7 +34,7 @@ func TestGetConsensusAddressMainNet(t *testing.T) { cfg, err := config.Load("../../config", netmode.MainNet) require.NoError(t, err) - validators, err := getValidators(cfg.ProtocolConfiguration) + validators, err := validatorsFromConfig(cfg.ProtocolConfiguration) require.NoError(t, err) script, err := getNextConsensusAddress(validators) diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index cf7f39e2a..e09ae2727 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -85,7 +85,7 @@ func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances { func (chain testChain) GetValidators() ([]*keys.PublicKey, error) { panic("TODO") } -func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) { +func (chain testChain) GetStandByValidators() keys.PublicKeys { panic("TODO") } func (chain testChain) GetEnrollments() ([]state.Validator, error) { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index f4b38862c..4ab2361e4 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -577,8 +577,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, check: func(t *testing.T, e *executor, validators interface{}) { var expected []result.Validator - sBValidators, err := e.chain.GetStandByValidators() - require.NoError(t, err) + sBValidators := e.chain.GetStandByValidators() for _, sbValidator := range sBValidators { expected = append(expected, result.Validator{ PublicKey: *sbValidator, From d0331bf21b0a5379a066f99afff5c754603a3221 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 18:30:42 +0300 Subject: [PATCH 07/18] native: simplify votes saving in NEO contract Follow C# behavior and fix state mismatch at block 11561 of preview2 testnet. --- pkg/core/native/native_neo.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index eed219825..b8a03ba85 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" @@ -218,10 +219,8 @@ func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey return errors.New("already registered") } si = new(state.StorageItem) - // It's the same simple counter, calling it `Votes` instead of `Balance` - // doesn't help a lot. - votes := state.NEP5BalanceState{} - si.Value = votes.Bytes() + // Zero value. + si.Value = []byte{} return ic.DAO.PutStorageItem(n.ContractID, key, si) } @@ -319,12 +318,9 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *b if si == nil { return errors.New("invalid validator") } - votes, err := state.NEP5BalanceStateFromBytes(si.Value) - if err != nil { - return err - } - votes.Balance.Add(&votes.Balance, value) - si.Value = votes.Bytes() + votes := bigint.FromBytes(si.Value) + votes.Add(votes, value) + si.Value = bigint.ToPreallocatedBytes(votes, si.Value[:0]) if err := d.PutStorageItem(n.ContractID, key, si); err != nil { return err } @@ -339,11 +335,8 @@ func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) { } arr := make([]keyWithVotes, 0, len(siMap)) for key, si := range siMap { - votes, err := state.NEP5BalanceStateFromBytes(si.Value) - if err != nil { - return nil, err - } - arr = append(arr, keyWithVotes{key, &votes.Balance}) + votes := bigint.FromBytes(si.Value) + arr = append(arr, keyWithVotes{key, votes}) } sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 }) return arr, nil From 37d44b94e1ac7b42814715e2bcd121bd8b13a776 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 18:40:28 +0300 Subject: [PATCH 08/18] native: fix netfee gas redistribution Preview2 testnet: file BlockStorage_100000/dump-block-12000.json: block 11562: key mismatch: feffffff1454a6cb279fbcedc66162ad4ad5d1d910202b92743e000000000000000000000005 vs feffffff1431b7e7aea5131f74721e002c6a56b610885813f79e000000000000000000000005 Originally this code was written to run after transactions processing, but after 0fa4c497352bd225c5035c0aa453d5fc3ee5d80f it works in different manner. --- pkg/core/native/native_gas.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index d5eef961c..000f365d1 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -87,7 +87,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error { absAmount := big.NewInt(int64(tx.SystemFee + tx.NetworkFee)) g.burn(ic, tx.Sender, absAmount) } - validators, err := g.NEO.GetValidatorsInternal(ic.Chain, ic.DAO) + validators, err := g.NEO.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO) if err != nil { return fmt.Errorf("cannot get block validators: %v", err) } From 5e56c9db299c229181873411362873745cdbf893 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 19:41:55 +0300 Subject: [PATCH 09/18] native: fix voting accounting in transfers processing We should modify votes count exactly by the amount of NEO balance change, it might be negative here. --- pkg/core/native/native_neo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index b8a03ba85..fcd256137 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -162,7 +162,7 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto return nil } if len(acc.Votes) > 0 { - if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil { + if err := n.ModifyAccountVotes(acc, ic.DAO, amount); err != nil { return err } siVC := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) From d5c9449a4334282bd09db6c89d4ff8d357d9b1c3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 21:09:37 +0300 Subject: [PATCH 10/18] dao: combine GetNextContractID and PutNextContractID And fix contract create to really update the ID, eliminating this difference in the storage (preview2 testnet): file BlockStorage_100000/dump-block-39000.json: block 38043: key mismatch: 0c000000617373657464df4ebe92334d1fc7e64b10f1d1e33942d9905e510000000000000009 vs 00000000617373657464df4ebe92334d1fc7e64b10f1d1e33942d9905e510000000000000009 --- pkg/core/dao/dao.go | 27 ++++++++++----------------- pkg/core/dao/dao_test.go | 12 +++++++----- pkg/core/interop_neo.go | 5 +---- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 978121feb..f24c7267c 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -32,7 +32,7 @@ type DAO interface { GetHeaderHashes() ([]util.Uint256, error) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) - GetNextContractID() (int32, error) + GetAndUpdateNextContractID() (int32, error) GetStorageItem(id int32, key []byte) *state.StorageItem GetStorageItems(id int32) (map[string]*state.StorageItem, error) GetStorageItemsWithPrefix(id int32, prefix []byte) (map[string]*state.StorageItem, error) @@ -47,7 +47,6 @@ type DAO interface { PutCurrentHeader(hashAndIndex []byte) error PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error - PutNextContractID(id int32) error PutStorageItem(id int32, key []byte, si *state.StorageItem) error PutVersion(v string) error StoreAsBlock(block *block.Block) error @@ -173,25 +172,19 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error { return dao.Store.Delete(key) } -// GetNextContractID returns id for the next contract and increases stored id. -func (dao *Simple) GetNextContractID() (int32, error) { +// GetAndUpdateNextContractID returns id for the next contract and increases stored ID. +func (dao *Simple) GetAndUpdateNextContractID() (int32, error) { + var id int32 key := storage.SYSContractID.Bytes() data, err := dao.Store.Get(key) - if err != nil { - if err == storage.ErrKeyNotFound { - err = nil - } + if err == nil { + id = int32(binary.LittleEndian.Uint32(data)) + } else if err != storage.ErrKeyNotFound { return 0, err } - return int32(binary.LittleEndian.Uint32(data)), nil -} - -// PutNextContractID sets next contract id to id. -func (dao *Simple) PutNextContractID(id int32) error { - key := storage.SYSContractID.Bytes() - data := make([]byte, 4) - binary.LittleEndian.PutUint32(data, uint32(id)) - return dao.Store.Put(key, data) + data = make([]byte, 4) + binary.LittleEndian.PutUint32(data, uint32(id+1)) + return id, dao.Store.Put(key, data) } // -- end contracts. diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 2da5f7f70..a94649467 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -84,15 +84,17 @@ func TestDeleteContractState(t *testing.T) { require.Nil(t, gotContractState) } -func TestSimple_GetNextContractID(t *testing.T) { +func TestSimple_GetAndUpdateNextContractID(t *testing.T) { dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet) - id, err := dao.GetNextContractID() + id, err := dao.GetAndUpdateNextContractID() require.NoError(t, err) require.EqualValues(t, 0, id) - require.NoError(t, dao.PutNextContractID(10)) - id, err = dao.GetNextContractID() + id, err = dao.GetAndUpdateNextContractID() require.NoError(t, err) - require.EqualValues(t, 10, id) + require.EqualValues(t, 1, id) + id, err = dao.GetAndUpdateNextContractID() + require.NoError(t, err) + require.EqualValues(t, 2, id) } func TestPutGetAppExecResult(t *testing.T) { diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index a2ad11d09..0c77bbc23 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -91,14 +91,11 @@ func contractCreate(ic *interop.Context, v *vm.VM) error { if contract != nil { return errors.New("contract already exists") } - id, err := ic.DAO.GetNextContractID() + id, err := ic.DAO.GetAndUpdateNextContractID() if err != nil { return err } newcontract.ID = id - if err := ic.DAO.PutNextContractID(id); err != nil { - return err - } if err := ic.DAO.PutContractState(newcontract); err != nil { return err } From a77357227aa217d3f5dbfa309cbac580949fa3ba Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 21:39:26 +0300 Subject: [PATCH 11/18] vm: fix caller's script hash returned from VM Regular CALLs don't update it, only Contract.Call does. Fixes the following mismatch on preview2 testnet: file BlockStorage_100000/dump-block-49000.json: block 48644, changes number mismatch: 6 vs 4 --- pkg/vm/context.go | 3 +++ pkg/vm/vm.go | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 8fa7afd98..bfbf2f2bb 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -41,6 +41,9 @@ type Context struct { // Script hash of the prog. scriptHash util.Uint160 + // Caller's contract script hash. + callingScriptHash util.Uint160 + // Call flags this context was created with. callFlag smartcontract.CallFlag } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 1c201dfd7..90d16014b 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -276,9 +276,11 @@ func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) { // given script hash directly into the Context to avoid its recalculations. It's // up to user of this function to make sure the script and hash match each other. func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f smartcontract.CallFlag) { + shash := v.GetCurrentScriptHash() v.LoadScriptWithFlags(b, f) ctx := v.Context() ctx.scriptHash = hash + ctx.callingScriptHash = shash } // Context returns the current executed context. Nil if there is no context, @@ -1607,7 +1609,7 @@ func (v *VM) bytesToPublicKey(b []byte) *keys.PublicKey { // GetCallingScriptHash implements ScriptHashGetter interface func (v *VM) GetCallingScriptHash() util.Uint160 { - return v.getContextScriptHash(1) + return v.Context().callingScriptHash } // GetEntryScriptHash implements ScriptHashGetter interface From fccad11716ed4117a9b9b654d7ac3391e5e707d8 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 21:57:05 +0300 Subject: [PATCH 12/18] native: drop accounts with zero balance They make no sense. Fixes preview2 testnet state problem: file BlockStorage_100000/dump-block-70000.json: block 69935: state mismatch for key ffffffff1454a6cb279fbcedc66162ad4ad5d1d910202b92743e000000000000000000000005: Deleted vs Added --- pkg/core/native/native_gas.go | 6 +++++- pkg/core/native/native_neo.go | 6 +++++- pkg/core/native/native_nep5.go | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 000f365d1..08683863a 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -58,7 +58,11 @@ func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.Stor return errors.New("insufficient funds") } acc.Balance.Add(&acc.Balance, amount) - si.Value = acc.Bytes() + if acc.Balance.Sign() != 0 { + si.Value = acc.Bytes() + } else { + si.Value = nil + } return nil } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index fcd256137..55c119b3f 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -180,7 +180,11 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto } } acc.Balance.Add(&acc.Balance, amount) - si.Value = acc.Bytes() + if acc.Balance.Sign() != 0 { + si.Value = acc.Bytes() + } else { + si.Value = nil + } return nil } diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index 597a407e0..a1db0ee7d 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -180,7 +180,12 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a if err := c.incBalance(ic, from, siFrom, inc); err != nil { return err } - if err := ic.DAO.PutStorageItem(c.ContractID, keyFrom, siFrom); err != nil { + if siFrom.Value == nil { + err = ic.DAO.DeleteStorageItem(c.ContractID, keyFrom) + } else { + err = ic.DAO.PutStorageItem(c.ContractID, keyFrom, siFrom) + } + if err != nil { return err } @@ -193,7 +198,12 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a if err := c.incBalance(ic, to, siTo, amount); err != nil { return err } - if err := ic.DAO.PutStorageItem(c.ContractID, keyTo, siTo); err != nil { + if siTo.Value == nil { + err = ic.DAO.DeleteStorageItem(c.ContractID, keyTo) + } else { + err = ic.DAO.PutStorageItem(c.ContractID, keyTo, siTo) + } + if err != nil { return err } } From 48fac6f87d2919665567e743845795299b43faa3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 22:20:17 +0300 Subject: [PATCH 13/18] native: unify some transfer code --- pkg/core/native/native_nep5.go | 53 +++++++++++++++------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index a1db0ee7d..5fa0a74b3 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -148,6 +148,28 @@ func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint1 ic.Notifications = append(ic.Notifications, ne) } +func (c *nep5TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint160, amount *big.Int) error { + key := makeAccountKey(acc) + si := ic.DAO.GetStorageItem(c.ContractID, key) + if si == nil { + if amount.Sign() <= 0 { + return errors.New("insufficient funds") + } + si = new(state.StorageItem) + } + + err := c.incBalance(ic, acc, si, amount) + if err != nil { + return err + } + if si.Value == nil { + err = ic.DAO.DeleteStorageItem(c.ContractID, key) + } else { + err = ic.DAO.PutStorageItem(c.ContractID, key, si) + } + return err +} + func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, amount *big.Int) error { if amount.Sign() == -1 { return errors.New("negative amount") @@ -164,12 +186,6 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a return errors.New("invalid signature") } - keyFrom := makeAccountKey(from) - siFrom := ic.DAO.GetStorageItem(c.ContractID, keyFrom) - if siFrom == nil { - return errors.New("insufficient funds") - } - isEmpty := from.Equals(to) || amount.Sign() == 0 inc := amount if isEmpty { @@ -177,33 +193,12 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a } else { inc = new(big.Int).Neg(inc) } - if err := c.incBalance(ic, from, siFrom, inc); err != nil { - return err - } - if siFrom.Value == nil { - err = ic.DAO.DeleteStorageItem(c.ContractID, keyFrom) - } else { - err = ic.DAO.PutStorageItem(c.ContractID, keyFrom, siFrom) - } - if err != nil { + if err := c.updateAccBalance(ic, from, inc); err != nil { return err } if !isEmpty { - keyTo := makeAccountKey(to) - siTo := ic.DAO.GetStorageItem(c.ContractID, keyTo) - if siTo == nil { - siTo = new(state.StorageItem) - } - if err := c.incBalance(ic, to, siTo, amount); err != nil { - return err - } - if siTo.Value == nil { - err = ic.DAO.DeleteStorageItem(c.ContractID, keyTo) - } else { - err = ic.DAO.PutStorageItem(c.ContractID, keyTo, siTo) - } - if err != nil { + if err := c.updateAccBalance(ic, to, amount); err != nil { return err } } From 7987cdadc0ee39f3ab4091b51be6f6634324c8fb Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 23 Jun 2020 22:31:57 +0300 Subject: [PATCH 14/18] native: update storage item value after gas distribution Even if the value is zero, the GAS distribution updates the balance height, so storage item must be updated too. Fixes the followin on preview2 testnet: block 74227: value mismatch for key ffffffff1454a6cb279fbcedc66162ad4ad5d1d910202b92743e000000000000000000000005: 1041032104809fd5002103f3210128010000 vs 1041032104809fd50021033f110128010000 --- pkg/core/native/native_neo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 55c119b3f..10eb38ac8 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -159,6 +159,7 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto return err } if amount.Sign() == 0 { + si.Value = acc.Bytes() return nil } if len(acc.Votes) > 0 { From 6f5a42facf441ef6ce108ee1555d07ea3eea366f Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 24 Jun 2020 00:05:27 +0300 Subject: [PATCH 15/18] smartcontract: correctly encode/decode AnyType It might get emitted with notifications. --- pkg/smartcontract/parameter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index c4dfe7fc5..56072d16a 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -212,7 +212,7 @@ func (p *Parameter) EncodeBinary(w *io.BinWriter) { w.WriteBytes(p.Value.(util.Uint160).BytesBE()) case Hash256Type: w.WriteBytes(p.Value.(util.Uint256).BytesBE()) - case InteropInterfaceType: + case InteropInterfaceType, AnyType: default: w.Err = fmt.Errorf("unknown type: %x", p.Type) } @@ -251,7 +251,7 @@ func (p *Parameter) DecodeBinary(r *io.BinReader) { var u util.Uint256 r.ReadBytes(u[:]) p.Value = u - case InteropInterfaceType: + case InteropInterfaceType, AnyType: default: r.Err = fmt.Errorf("unknown type: %x", p.Type) } From 954c8ff8d6bc5dec74b2ae42acc60398cdc965ca Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 24 Jun 2020 08:57:04 +0300 Subject: [PATCH 16/18] compiler: support nil checks --- pkg/compiler/codegen.go | 22 ++++++++++++-- pkg/compiler/nilcheck_test.go | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 pkg/compiler/nilcheck_test.go diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 96f9f87e5..b30513dda 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -659,8 +659,26 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { return nil } - ast.Walk(c, n.X) - ast.Walk(c, n.Y) + var checkForNull bool + + if isExprNil(n.X) { + checkForNull = true + } else { + ast.Walk(c, n.X) + } + if isExprNil(n.Y) { + checkForNull = true + } else { + ast.Walk(c, n.Y) + } + if checkForNull { + emit.Opcode(c.prog.BinWriter, opcode.ISNULL) + if n.Op == token.NEQ { + emit.Opcode(c.prog.BinWriter, opcode.NOT) + } + + return nil + } switch { case n.Op == token.ADD: diff --git a/pkg/compiler/nilcheck_test.go b/pkg/compiler/nilcheck_test.go new file mode 100644 index 000000000..54f36e037 --- /dev/null +++ b/pkg/compiler/nilcheck_test.go @@ -0,0 +1,55 @@ +package compiler_test + +import ( + "math/big" + "testing" +) + +var nilTestCases = []testCase{ + { + "nil check positive right", + ` + package foo + func Main() int { + var t interface{} + if t == nil { + return 1 + } + return 2 + } + `, + big.NewInt(1), + }, + { + "nil check negative right", + ` + package foo + func Main() int { + t := []byte{} + if t == nil { + return 1 + } + return 2 + } + `, + big.NewInt(2), + }, + { + "nil check positive left", + ` + package foo + func Main() int { + var t interface{} + if nil == t { + return 1 + } + return 2 + } + `, + big.NewInt(1), + }, +} + +func TestNil(t *testing.T) { + runTestCases(t, nilTestCases) +} From d81d826bfccd6a76593f586f02f6424e585a953e Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 24 Jun 2020 00:06:42 +0300 Subject: [PATCH 17/18] core: fix Storage.Get to return Null when there is no value Match C# implementation and fix state inconsistency at block 249920 of preview2 testnet. Make our Go Storage.Get return nil and adapt examples/tests. --- docs/compiler.md | 6 +++- examples/token-sale/token_sale.go | 38 ++++++++++++++-------- examples/token/nep5/nep5.go | 23 +++++++++---- pkg/core/interop_system.go | 2 +- pkg/interop/storage/storage.go | 6 ++-- pkg/rpc/server/server_test.go | 4 +-- pkg/rpc/server/testdata/test_contract.avm | Bin 727 -> 756 bytes pkg/rpc/server/testdata/test_contract.go | 18 ++++++++-- pkg/rpc/server/testdata/testblocks.acc | Bin 6781 -> 6810 bytes 9 files changed, 67 insertions(+), 30 deletions(-) diff --git a/docs/compiler.md b/docs/compiler.md index 58711237b..56f5367e5 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -236,7 +236,11 @@ type Token struct { func (t Token) AddToCirculation(amount int) bool { ctx := storage.Context() - inCirc := storage.Get(ctx, "in_circ").(int) + var inCirc int + val := storage.Get(ctx, "in_circ") + if val != nil { + inCirc = val.(int) + } inCirc += amount storage.Put(ctx, "in_circ", inCirc) return true diff --git a/examples/token-sale/token_sale.go b/examples/token-sale/token_sale.go index 84732bad9..a9d5e7727 100644 --- a/examples/token-sale/token_sale.go +++ b/examples/token-sale/token_sale.go @@ -71,15 +71,25 @@ func NewTokenConfig() TokenConfig { } } +// getIntFromDB is a helper that checks for nil result of storage.Get and returns +// zero as the default value. +func getIntFromDB(ctx storage.Context, key []byte) int { + var res int + val := storage.Get(ctx, key) + if val != nil { + res = val.(int) + } + return res +} + // InCirculation return the amount of total tokens that are in circulation. func (t TokenConfig) InCirculation(ctx storage.Context) int { - amount := storage.Get(ctx, t.CirculationKey) - return amount.(int) + return getIntFromDB(ctx, t.CirculationKey) } // AddToCirculation sets the given amount as "in circulation" in the storage. func (t TokenConfig) AddToCirculation(ctx storage.Context, amount int) bool { - supply := storage.Get(ctx, t.CirculationKey).(int) + supply := getIntFromDB(ctx, t.CirculationKey) supply += amount storage.Put(ctx, t.CirculationKey, supply) return true @@ -88,8 +98,8 @@ func (t TokenConfig) AddToCirculation(ctx storage.Context, amount int) bool { // TokenSaleAvailableAmount returns the total amount of available tokens left // to be distributed. func (t TokenConfig) TokenSaleAvailableAmount(ctx storage.Context) int { - inCirc := storage.Get(ctx, t.CirculationKey) - return t.TotalSupply - inCirc.(int) + inCirc := getIntFromDB(ctx, t.CirculationKey) + return t.TotalSupply - inCirc } // Main smart contract entry point. @@ -128,11 +138,11 @@ func handleOperation(op string, args []interface{}, ctx storage.Context, cfg Tok return cfg.Symbol } if op == "totalSupply" { - return storage.Get(ctx, cfg.CirculationKey) + return getIntFromDB(ctx, cfg.CirculationKey) } if op == "balanceOf" { if len(args) == 1 { - return storage.Get(ctx, args[0].([]byte)) + return getIntFromDB(ctx, args[0].([]byte)) } } if op == "transfer" { @@ -177,7 +187,7 @@ func transfer(cfg TokenConfig, ctx storage.Context, from, to []byte, amount int) if amount <= 0 || len(to) != 20 || !runtime.CheckWitness(from) { return false } - amountFrom := storage.Get(ctx, from).(int) + amountFrom := getIntFromDB(ctx, from) if amountFrom < amount { return false } @@ -187,7 +197,7 @@ func transfer(cfg TokenConfig, ctx storage.Context, from, to []byte, amount int) diff := amountFrom - amount storage.Put(ctx, from, diff) } - amountTo := storage.Get(ctx, to).(int) + amountTo := getIntFromDB(ctx, to) totalAmountTo := amountTo + amount storage.Put(ctx, to, totalAmountTo) return true @@ -201,15 +211,15 @@ func transferFrom(cfg TokenConfig, ctx storage.Context, from, to []byte, amount if len(availableKey) != 40 { return false } - availableTo := storage.Get(ctx, availableKey).(int) + availableTo := getIntFromDB(ctx, availableKey) if availableTo < amount { return false } - fromBalance := storage.Get(ctx, from).(int) + fromBalance := getIntFromDB(ctx, from) if fromBalance < amount { return false } - toBalance := storage.Get(ctx, to).(int) + toBalance := getIntFromDB(ctx, to) newFromBalance := fromBalance - amount newToBalance := toBalance + amount storage.Put(ctx, to, newToBalance) @@ -231,7 +241,7 @@ func approve(ctx storage.Context, owner, spender []byte, amount int) bool { if len(spender) != 20 { return false } - toSpend := storage.Get(ctx, owner).(int) + toSpend := getIntFromDB(ctx, owner) if toSpend < amount { return false } @@ -246,5 +256,5 @@ func approve(ctx storage.Context, owner, spender []byte, amount int) bool { func allowance(ctx storage.Context, from, to []byte) int { key := append(from, to...) - return storage.Get(ctx, key).(int) + return getIntFromDB(ctx, key) } diff --git a/examples/token/nep5/nep5.go b/examples/token/nep5/nep5.go index 7082ad65b..89c92994c 100644 --- a/examples/token/nep5/nep5.go +++ b/examples/token/nep5/nep5.go @@ -22,14 +22,25 @@ type Token struct { CirculationKey string } +// getIntFromDB is a helper that checks for nil result of storage.Get and returns +// zero as the default value. +func getIntFromDB(ctx storage.Context, key []byte) int { + var res int + val := storage.Get(ctx, key) + if val != nil { + res = val.(int) + } + return res +} + // GetSupply gets the token totalSupply value from VM storage func (t Token) GetSupply(ctx storage.Context) interface{} { - return storage.Get(ctx, t.CirculationKey) + return getIntFromDB(ctx, []byte(t.CirculationKey)) } // BalanceOf gets the token balance of a specific address func (t Token) BalanceOf(ctx storage.Context, hodler []byte) interface{} { - return storage.Get(ctx, hodler) + return getIntFromDB(ctx, hodler) } // Transfer token from one user to another @@ -48,7 +59,7 @@ func (t Token) Transfer(ctx storage.Context, from []byte, to []byte, amount int) storage.Put(ctx, from, diff) } - amountTo := storage.Get(ctx, to).(int) + amountTo := getIntFromDB(ctx, to) totalAmountTo := amountTo + amount storage.Put(ctx, to, totalAmountTo) runtime.Notify("transfer", from, to, amount) @@ -61,7 +72,7 @@ func (t Token) CanTransfer(ctx storage.Context, from []byte, to []byte, amount i return -1 } - amountFrom := storage.Get(ctx, from).(int) + amountFrom := getIntFromDB(ctx, from) if amountFrom < amount { return -1 } @@ -98,8 +109,8 @@ func (t Token) Mint(ctx storage.Context, to []byte) bool { if !IsUsableAddress(t.Owner) { return false } - minted := storage.Get(ctx, []byte("minted")).(bool) - if minted { + minted := storage.Get(ctx, []byte("minted")) + if minted != nil && minted.(bool) == true { return false } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index f51d6fceb..66fd0e478 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -289,7 +289,7 @@ func storageGet(ic *interop.Context, v *vm.VM) error { if si != nil && si.Value != nil { v.Estack().PushVal(si.Value) } else { - v.Estack().PushVal([]byte{}) + v.Estack().PushVal(stackitem.Null{}) } return nil } diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index 748157ef6..eb6e1faa6 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -38,9 +38,9 @@ func GetReadOnlyContext() Context { return Context{} } func Put(ctx Context, key interface{}, value interface{}) {} // Get retrieves value stored for the given key using given Context. See Put -// documentation on possible key and value types. This function uses -// `System.Storage.Get` syscall. -func Get(ctx Context, key interface{}) interface{} { return 0 } +// documentation on possible key and value types. If the value is not present in +// the database it returns nil. This function uses `System.Storage.Get` syscall. +func Get(ctx Context, key interface{}) interface{} { return nil } // Delete removes key-value pair from storage by the given key using given // Context. See Put documentation on possible key types. This function uses diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 4ab2361e4..80c727c0d 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -50,8 +50,8 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "e65ff7b3a02d207b584a5c27057d4e9862ef01da" -const deploymentTxHash = "b0428600383ec7f7b06734978a24dbe81edc87b781f58c0614f122c735d8cf6a" +const testContractHash = "10e262ef80c76bdecca287a2c047841fc02c3129" +const deploymentTxHash = "ad8b149c799d4b2337162b0ad23e0ba8845cddb9cfca8a45587ee207015d2a7c" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { diff --git a/pkg/rpc/server/testdata/test_contract.avm b/pkg/rpc/server/testdata/test_contract.avm index eec2f3ea44896e5efd3bca6d65da162028afd9bc..bdb814d371c7b843a5aaa7f0364df89fdeeb98ee 100755 GIT binary patch literal 756 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 Iv0KUi0`Qq*lmGw# From 5251607fb71f6ae9ffb64549f7867438a2c5f6b4 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 24 Jun 2020 09:03:10 +0300 Subject: [PATCH 18/18] transaction: s/txid/hash/ for JSON to match C# implementation It uses `hash` for transactions now, but `txid` for application logs. --- pkg/core/transaction/transaction.go | 2 +- pkg/rpc/client/rpc_test.go | 4 ++-- pkg/rpc/client/wsclient_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index ce4f1a46a..96c0864d1 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -284,7 +284,7 @@ func (t *Transaction) FeePerByte() util.Fixed8 { // transactionJSON is a wrapper for Transaction and // used for correct marhalling of transaction.Data type transactionJSON struct { - TxID util.Uint256 `json:"txid"` + TxID util.Uint256 `json:"hash"` Size int `json:"size"` Version uint8 `json:"version"` Nonce uint32 `json:"nonce"` diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index d2749af36..8995948de 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -41,13 +41,13 @@ const hexB1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54 const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" -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=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]},{"txid":"0xb661d5e4d9e41c3059b068f8abb6f1566a47ec800879e34c0ebd2799781a2760","size":579,"version":0,"nonce":3,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0088035","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBQxboUQOQGdOd/Cw31sP+4Z/VgJhwwUq+xTYvEedbbgLkB7uY1jZ10UOEETwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4","scripts":[{"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":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=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"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","sys_fee":"0","net_fee":"0.0088035","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBQxboUQOQGdOd/Cw31sP+4Z/VgJhwwUq+xTYvEedbbgLkB7uY1jZ10UOEETwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4","scripts":[{"invocation":"DECHEOe12Kxs2NCdb7Nb8tzOX1+zhZXdttBKVwvJJbwsVac83188taH+sKzE8myLvKbEPfO0qYuMPCyAnC8JbrJaDEDBhsECy/cjE/2U31B3/Fu+/NMiJ+0hWaR6RllId/o58zDYIjtFqiSv8AW+u1skJ6UMjE3oYY59ewDXPYNsRJQuDECiQ7W1Zb1LwvC7ES92JPazUUwSQTxalSMIGfrOP3YPA/y5axiPmAOKPyUWhrU6iNaXRPTkqYXmKXADqAzbFp6ADEBKlR5hrJnV7jGDHREXVK23EbSpBgqVJP44OpB3GEPNsJY4JnQCeofyoVqDvXfesrKrH+iz3m5UYpPvPfmxEp4o","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}]}}` const hexHeader1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54442fd56401107247de4e35599d1feffaf6e763972f738d2858b0a22ad06523867e4dcf921f7c1c67201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c400f01eb6371a135527ddb205a0dae1f69d2d324837cce128bead9033091883f9ce21e6d759a40f690746592b021d397f088e3b7b417fcef73cd1bfdc2800769520c4084f7ae5d9f58a09aa56eeb2f282744a59a82d17e3be0eb1c7f54e6e8df79620e2608191bac57280a836db2ec2a776d07e43f16bc76c47d348b0fcd09d56c7c320c402b95b4e39ec35162a640795ff223e92dec95390a072eb457f6a4323052f80731517e0df029e4a457204f777f5261c6a4a88d46d4c3abdec635a6eed580d6c77f0c40b9735745b1dd795a258c31d8e6fa87d5cfc2e9b3a4890d610d33bcf833b64c58b0c5cea17f3a7128f1065ed193e636971590f193f28bdd1eeebbbc1fe6e7cee594130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00" 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 txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","confirmations":6,"blocktime":1592472500001,"txid":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"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":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","confirmations":6,"blocktime":1592472500001,"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}}` // getResultBlock1 returns data for block number 1 which is used by several tests. func getResultBlock1() *result.Block { diff --git a/pkg/rpc/client/wsclient_test.go b/pkg/rpc/client/wsclient_test.go index f2221a675..50fed7ccf 100644 --- a/pkg/rpc/client/wsclient_test.go +++ b/pkg/rpc/client/wsclient_test.go @@ -119,7 +119,7 @@ func TestWSClientEvents(t *testing.T) { `{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","trigger":"Application","vmstate":"HALT","gas_consumed":"2.291","stack":[],"notifications":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"Y29udHJhY3QgY2FsbA=="},{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteArray","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}},{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"ByteArray","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}}]}]}`, `{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"Y29udHJhY3QgY2FsbA=="},{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteArray","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}}]}`, `{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","trigger":"Application","vmstate":"HALT","gas_consumed":"0.0604261","stack":[],"notifications":[{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","state":{"type":"Array","value":[{"type":"ByteArray","value":"Y29udHJhY3QgY2FsbA=="},{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteArray","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}]}},{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","state":{"type":"Array","value":[{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteArray","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=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","size":265,"version":0,"nonce":9,"sender":"NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":"CalledByEntry"}],"script":"AHsMFCBygnSvr8NvQ6Bx0yjPo+Yp2cuwDBQxboUQOQGdOd/Cw31sP+4Z/VgJhxPADAh0cmFuc2ZlcgwU2gHvYphOfQUnXEpYeyAtoLP3X+ZBYn1bUjg=","scripts":[{"invocation":"DECwklSj3liZOJbktRtkVdUCu8U2LQlrU6Dv8NtMgd0xXbk5lXjc2p68xv6xtJXbJ4aoFMJZ9lkcNpGoeUCcaCet","verification":"DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQatQ="}]}]}]}`, + `{"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=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","size":265,"version":0,"nonce":9,"sender":"NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":"CalledByEntry"}],"script":"AHsMFCBygnSvr8NvQ6Bx0yjPo+Yp2cuwDBQxboUQOQGdOd/Cw31sP+4Z/VgJhxPADAh0cmFuc2ZlcgwU2gHvYphOfQUnXEpYeyAtoLP3X+ZBYn1bUjg=","scripts":[{"invocation":"DECwklSj3liZOJbktRtkVdUCu8U2LQlrU6Dv8NtMgd0xXbk5lXjc2p68xv6xtJXbJ4aoFMJZ9lkcNpGoeUCcaCet","verification":"DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQatQ="}]}]}]}`, `{"jsonrpc":"2.0","method":"event_missed","params":[]}`, } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {