From f1cdcbc99c7f929b0c6f3dfcd132e06489fb9f0b Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 18 Jun 2020 13:50:30 +0300 Subject: [PATCH] core: store smartcontract items by id closes #1037 --- pkg/core/blockchain.go | 10 ++-- pkg/core/blockchainer/blockchainer.go | 4 +- pkg/core/dao/cacheddao_test.go | 16 +++---- pkg/core/dao/dao.go | 47 ++++++++++-------- pkg/core/dao/dao_test.go | 28 ++++++++--- pkg/core/interop_neo.go | 20 ++------ pkg/core/interop_neo_test.go | 20 ++++---- pkg/core/interop_system.go | 69 ++++++++++++--------------- pkg/core/native/native_neo.go | 32 ++++++------- pkg/core/native/native_nep5.go | 16 +++---- pkg/network/helper_test.go | 4 +- pkg/rpc/server/server.go | 40 +++++++++++++--- 12 files changed, 167 insertions(+), 139 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 19531bca9..1144b43f9 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -869,13 +869,13 @@ func (bc *Blockchain) GetAppExecResult(hash util.Uint256) (*state.AppExecResult, } // GetStorageItem returns an item from storage. -func (bc *Blockchain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem { - return bc.dao.GetStorageItem(scripthash, key) +func (bc *Blockchain) GetStorageItem(id int32, key []byte) *state.StorageItem { + return bc.dao.GetStorageItem(id, key) } -// GetStorageItems returns all storage items for a given scripthash. -func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { - return bc.dao.GetStorageItems(hash) +// GetStorageItems returns all storage items for a given contract id. +func (bc *Blockchain) GetStorageItems(id int32) (map[string]*state.StorageItem, error) { + return bc.dao.GetStorageItems(id) } // GetBlock returns a Block by the given hash. diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index 2a9664246..bbe30a972 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -39,8 +39,8 @@ type Blockchainer interface { GetValidators() ([]*keys.PublicKey, error) GetStandByValidators() (keys.PublicKeys, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) - GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem - GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) + GetStorageItem(id int32, key []byte) *state.StorageItem + GetStorageItems(id int32) (map[string]*state.StorageItem, error) GetTestVM(tx *transaction.Transaction) *vm.VM GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) mempool.Feer // fee interface diff --git a/pkg/core/dao/cacheddao_test.go b/pkg/core/dao/cacheddao_test.go index ceee31b6c..236372b2f 100644 --- a/pkg/core/dao/cacheddao_test.go +++ b/pkg/core/dao/cacheddao_test.go @@ -114,29 +114,29 @@ func TestCachedCachedDao(t *testing.T) { assert.NotEqual(t, pdao.Store, intDao.Store) assert.NotEqual(t, cdaoDao.Store, intDao.Store) - hash := random.Uint160() + id := int32(random.Int(0, 1024)) key := []byte("qwerty") si := &state.StorageItem{Value: []byte("poiuyt")} - require.NoError(t, ccdao.PutStorageItem(hash, key, si)) - resi := ccdao.GetStorageItem(hash, key) + require.NoError(t, ccdao.PutStorageItem(id, key, si)) + resi := ccdao.GetStorageItem(id, key) assert.Equal(t, si, resi) - resi = cdao.GetStorageItem(hash, key) + resi = cdao.GetStorageItem(id, key) assert.Equal(t, (*state.StorageItem)(nil), resi) - resi = pdao.GetStorageItem(hash, key) + resi = pdao.GetStorageItem(id, key) assert.Equal(t, (*state.StorageItem)(nil), resi) cnt, err := ccdao.Persist() assert.NoError(t, err) assert.Equal(t, 1, cnt) - resi = cdao.GetStorageItem(hash, key) + resi = cdao.GetStorageItem(id, key) assert.Equal(t, si, resi) - resi = pdao.GetStorageItem(hash, key) + resi = pdao.GetStorageItem(id, key) assert.Equal(t, (*state.StorageItem)(nil), resi) cnt, err = cdao.Persist() assert.NoError(t, err) assert.Equal(t, 1, cnt) - resi = pdao.GetStorageItem(hash, key) + resi = pdao.GetStorageItem(id, key) assert.Equal(t, si, resi) } diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 23b88ca54..978121feb 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -19,7 +19,7 @@ import ( type DAO interface { AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error) DeleteContractState(hash util.Uint160) error - DeleteStorageItem(scripthash util.Uint160, key []byte) error + DeleteStorageItem(id int32, key []byte) error GetAccountState(hash util.Uint160) (*state.Account, error) GetAccountStateOrNew(hash util.Uint160) (*state.Account, error) GetAndDecode(entity io.Serializable, key []byte) error @@ -33,9 +33,9 @@ type DAO interface { GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) GetNextContractID() (int32, error) - GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem - GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) - GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, 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) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) GetVersion() (string, error) GetWrapped() DAO @@ -48,7 +48,7 @@ type DAO interface { PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error PutNextContractID(id int32) error - PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error + PutStorageItem(id int32, key []byte, si *state.StorageItem) error PutVersion(v string) error StoreAsBlock(block *block.Block) error StoreAsCurrentBlock(block *block.Block) error @@ -296,8 +296,8 @@ func (dao *Simple) PutAppExecResult(aer *state.AppExecResult) error { // -- start storage item. // GetStorageItem returns StorageItem if it exists in the given store. -func (dao *Simple) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem { - b, err := dao.Store.Get(makeStorageItemKey(scripthash, key)) +func (dao *Simple) GetStorageItem(id int32, key []byte) *state.StorageItem { + b, err := dao.Store.Get(makeStorageItemKey(id, key)) if err != nil { return nil } @@ -312,30 +312,30 @@ func (dao *Simple) GetStorageItem(scripthash util.Uint160, key []byte) *state.St return si } -// PutStorageItem puts given StorageItem for given script with given +// PutStorageItem puts given StorageItem for given id with given // key into the given store. -func (dao *Simple) PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error { - return dao.Put(si, makeStorageItemKey(scripthash, key)) +func (dao *Simple) PutStorageItem(id int32, key []byte, si *state.StorageItem) error { + return dao.Put(si, makeStorageItemKey(id, key)) } -// DeleteStorageItem drops storage item for the given script with the +// DeleteStorageItem drops storage item for the given id with the // given key from the store. -func (dao *Simple) DeleteStorageItem(scripthash util.Uint160, key []byte) error { - return dao.Store.Delete(makeStorageItemKey(scripthash, key)) +func (dao *Simple) DeleteStorageItem(id int32, key []byte) error { + return dao.Store.Delete(makeStorageItemKey(id, key)) } -// GetStorageItems returns all storage items for a given scripthash. -func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { - return dao.GetStorageItemsWithPrefix(hash, nil) +// GetStorageItems returns all storage items for a given id. +func (dao *Simple) GetStorageItems(id int32) (map[string]*state.StorageItem, error) { + return dao.GetStorageItemsWithPrefix(id, nil) } -// GetStorageItemsWithPrefix returns all storage items with given prefix for a +// GetStorageItemsWithPrefix returns all storage items with given id for a // given scripthash. -func (dao *Simple) GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error) { +func (dao *Simple) GetStorageItemsWithPrefix(id int32, prefix []byte) (map[string]*state.StorageItem, error) { var siMap = make(map[string]*state.StorageItem) var err error - lookupKey := storage.AppendPrefix(storage.STStorage, hash.BytesLE()) + lookupKey := makeStorageItemKey(id, nil) if prefix != nil { lookupKey = append(lookupKey, prefix...) } @@ -362,8 +362,13 @@ func (dao *Simple) GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) ( } // makeStorageItemKey returns a key used to store StorageItem in the DB. -func makeStorageItemKey(scripthash util.Uint160, key []byte) []byte { - return storage.AppendPrefix(storage.STStorage, append(scripthash.BytesLE(), key...)) +func makeStorageItemKey(id int32, key []byte) []byte { + // 1 for prefix + 4 for Uint32 + len(key) for key + buf := make([]byte, 5+len(key)) + buf[0] = byte(storage.STStorage) + binary.LittleEndian.PutUint32(buf[1:], uint32(id)) + copy(buf[5:], key) + return buf } // -- end storage item. diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 7c7f2ab78..2da5f7f70 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -1,6 +1,7 @@ package dao import ( + "encoding/binary" "testing" "github.com/nspcc-dev/neo-go/pkg/config/netmode" @@ -111,25 +112,25 @@ func TestPutGetAppExecResult(t *testing.T) { func TestPutGetStorageItem(t *testing.T) { dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet) - hash := random.Uint160() + id := int32(random.Int(0, 1024)) key := []byte{0} storageItem := &state.StorageItem{Value: []uint8{}} - err := dao.PutStorageItem(hash, key, storageItem) + err := dao.PutStorageItem(id, key, storageItem) require.NoError(t, err) - gotStorageItem := dao.GetStorageItem(hash, key) + gotStorageItem := dao.GetStorageItem(id, key) require.Equal(t, storageItem, gotStorageItem) } func TestDeleteStorageItem(t *testing.T) { dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet) - hash := random.Uint160() + id := int32(random.Int(0, 1024)) key := []byte{0} storageItem := &state.StorageItem{Value: []uint8{}} - err := dao.PutStorageItem(hash, key, storageItem) + err := dao.PutStorageItem(id, key, storageItem) require.NoError(t, err) - err = dao.DeleteStorageItem(hash, key) + err = dao.DeleteStorageItem(id, key) require.NoError(t, err) - gotStorageItem := dao.GetStorageItem(hash, key) + gotStorageItem := dao.GetStorageItem(id, key) require.Nil(t, gotStorageItem) } @@ -208,3 +209,16 @@ func TestStoreAsTransaction(t *testing.T) { hasTransaction := dao.HasTransaction(hash) require.True(t, hasTransaction) } + +func TestMakeStorageItemKey(t *testing.T) { + var id int32 = 5 + + expected := []byte{byte(storage.STStorage), 0, 0, 0, 0, 1, 2, 3} + binary.LittleEndian.PutUint32(expected[1:5], uint32(id)) + actual := makeStorageItemKey(id, []byte{1, 2, 3}) + require.Equal(t, expected, actual) + + expected = expected[0:5] + actual = makeStorageItemKey(id, nil) + require.Equal(t, expected, actual) +} diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 6717c620c..a2ad11d09 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -34,12 +34,8 @@ func storageFind(ic *interop.Context, v *vm.VM) error { if !ok { return fmt.Errorf("%T is not a StorageContext", stcInterface) } - err := checkStorageContext(ic, stc) - if err != nil { - return err - } prefix := v.Estack().Pop().Bytes() - siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ScriptHash, prefix) + siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix) if err != nil { return err } @@ -138,19 +134,13 @@ func contractUpdate(ic *interop.Context, v *vm.VM) error { return err } } - if contract.HasStorage() { - // TODO store items by ID #1037 - hash := v.GetCurrentScriptHash() - siMap, err := ic.DAO.GetStorageItems(hash) + if !newcontract.HasStorage() { + siMap, err := ic.DAO.GetStorageItems(contract.ID) if err != nil { return err } - for k, v := range siMap { - v.IsConst = false - err = ic.DAO.PutStorageItem(contract.ScriptHash(), []byte(k), v) - if err != nil { - return err - } + if len(siMap) != 0 { + return errors.New("old contract shouldn't have storage") } } v.Estack().PushVal(stackitem.NewInterop(contract)) diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index f1451de6c..09e28f4f0 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -63,16 +63,16 @@ func TestStorageFind(t *testing.T) { require.NoError(t, context.DAO.PutContractState(contractState)) - scriptHash := contractState.ScriptHash() + id := contractState.ID for i := range skeys { - err := context.DAO.PutStorageItem(scriptHash, skeys[i], items[i]) + err := context.DAO.PutStorageItem(id, skeys[i], items[i]) require.NoError(t, err) } t.Run("normal invocation", func(t *testing.T) { v.Estack().PushVal([]byte{0x01}) - v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: scriptHash})) + v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id})) err := storageFind(context, v) require.NoError(t, err) @@ -110,7 +110,7 @@ func TestStorageFind(t *testing.T) { t.Run("normal invocation, empty result", func(t *testing.T) { v.Estack().PushVal([]byte{0x03}) - v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: scriptHash})) + v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id})) err := storageFind(context, v) require.NoError(t, err) @@ -126,14 +126,15 @@ func TestStorageFind(t *testing.T) { require.Error(t, storageFind(context, v)) }) - t.Run("invalid script hash", func(t *testing.T) { - invalidHash := scriptHash - invalidHash[0] = ^invalidHash[0] + t.Run("invalid id", func(t *testing.T) { + invalidID := id + 1 v.Estack().PushVal([]byte{0x01}) - v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: invalidHash})) + v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: invalidID})) - require.Error(t, storageFind(context, v)) + require.NoError(t, storageFind(context, v)) + require.NoError(t, enumerator.Next(context, v)) + require.False(t, v.Estack().Pop().Bool()) }) } @@ -257,6 +258,7 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.C contractState := &state.Contract{ Script: script, Manifest: *m, + ID: 123, } chain := newTestChain(t) diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index c0a66ed06..aaffb4ba0 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -28,11 +28,11 @@ const ( MaxTraceableBlocks = transaction.MaxValidUntilBlockIncrement ) -// StorageContext contains storing script hash and read/write flag, it's used as +// StorageContext contains storing id and read/write flag, it's used as // a context for storage manipulation functions. type StorageContext struct { - ScriptHash util.Uint160 - ReadOnly bool + ID int32 + ReadOnly bool } // getBlockHashFromElement converts given vm.Element to block hash using given @@ -259,17 +259,6 @@ func runtimeGetTime(ic *interop.Context, v *vm.VM) error { return nil } -func checkStorageContext(ic *interop.Context, stc *StorageContext) error { - contract, err := ic.DAO.GetContractState(stc.ScriptHash) - if err != nil { - return errors.New("no contract found") - } - if !contract.HasStorage() { - return fmt.Errorf("contract %s can't use storage", stc.ScriptHash) - } - return nil -} - // storageDelete deletes stored key-value pair. func storageDelete(ic *interop.Context, v *vm.VM) error { stcInterface := v.Estack().Pop().Value() @@ -280,16 +269,12 @@ func storageDelete(ic *interop.Context, v *vm.VM) error { if stc.ReadOnly { return errors.New("StorageContext is read only") } - err := checkStorageContext(ic, stc) - if err != nil { - return err - } key := v.Estack().Pop().Bytes() - si := ic.DAO.GetStorageItem(stc.ScriptHash, key) + si := ic.DAO.GetStorageItem(stc.ID, key) if si != nil && si.IsConst { return errors.New("storage item is constant") } - return ic.DAO.DeleteStorageItem(stc.ScriptHash, key) + return ic.DAO.DeleteStorageItem(stc.ID, key) } // storageGet returns stored key-value pair. @@ -299,12 +284,8 @@ func storageGet(ic *interop.Context, v *vm.VM) error { if !ok { return fmt.Errorf("%T is not a StorageContext", stcInterface) } - err := checkStorageContext(ic, stc) - if err != nil { - return err - } key := v.Estack().Pop().Bytes() - si := ic.DAO.GetStorageItem(stc.ScriptHash, key) + si := ic.DAO.GetStorageItem(stc.ID, key) if si != nil && si.Value != nil { v.Estack().PushVal(si.Value) } else { @@ -315,9 +296,16 @@ func storageGet(ic *interop.Context, v *vm.VM) error { // storageGetContext returns storage context (scripthash). func storageGetContext(ic *interop.Context, v *vm.VM) error { + contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) + if err != nil { + return err + } + if !contract.HasStorage() { + return err + } sc := &StorageContext{ - ScriptHash: v.GetCurrentScriptHash(), - ReadOnly: false, + ID: contract.ID, + ReadOnly: false, } v.Estack().PushVal(stackitem.NewInterop(sc)) return nil @@ -325,9 +313,16 @@ func storageGetContext(ic *interop.Context, v *vm.VM) error { // storageGetReadOnlyContext returns read-only context (scripthash). func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error { + contract, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) + if err != nil { + return err + } + if !contract.HasStorage() { + return err + } sc := &StorageContext{ - ScriptHash: v.GetCurrentScriptHash(), - ReadOnly: true, + ID: contract.ID, + ReadOnly: true, } v.Estack().PushVal(stackitem.NewInterop(sc)) return nil @@ -340,11 +335,7 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext, if stc.ReadOnly { return errors.New("StorageContext is read only") } - err := checkStorageContext(ic, stc) - if err != nil { - return err - } - si := ic.DAO.GetStorageItem(stc.ScriptHash, key) + si := ic.DAO.GetStorageItem(stc.ID, key) if si == nil { si = &state.StorageItem{} } @@ -360,7 +351,7 @@ func putWithContextAndFlags(ic *interop.Context, v *vm.VM, stc *StorageContext, } si.Value = value si.IsConst = isConst - return ic.DAO.PutStorageItem(stc.ScriptHash, key, si) + return ic.DAO.PutStorageItem(stc.ID, key, si) } // storagePutInternal is a unified implementation of storagePut and storagePutEx. @@ -398,8 +389,8 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error { } if !stc.ReadOnly { stx := &StorageContext{ - ScriptHash: stc.ScriptHash, - ReadOnly: true, + ID: stc.ID, + ReadOnly: true, } stc = stx } @@ -462,12 +453,12 @@ func contractDestroy(ic *interop.Context, v *vm.VM) error { return err } if cs.HasStorage() { - siMap, err := ic.DAO.GetStorageItems(hash) + siMap, err := ic.DAO.GetStorageItems(cs.ID) if err != nil { return err } for k := range siMap { - _ = ic.DAO.DeleteStorageItem(hash, []byte(k)) + _ = ic.DAO.DeleteStorageItem(cs.ID, []byte(k)) } } return nil diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index fab5f8404..e09481bc1 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -124,7 +124,7 @@ func (n *NEO) Initialize(ic *interop.Context) error { vc := new(ValidatorsCount) si.Value = vc.Bytes() - if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, &si); err != nil { + if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, &si); err != nil { return err } h, vs, err := getStandbyValidatorsHash(ic) @@ -150,7 +150,7 @@ func (n *NEO) OnPersist(ic *interop.Context) error { } si := new(state.StorageItem) si.Value = pubs.Bytes() - return ic.DAO.PutStorageItem(n.Hash, nextValidatorsKey, si) + return ic.DAO.PutStorageItem(n.ContractID, nextValidatorsKey, si) } func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error { @@ -171,7 +171,7 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil { return err } - siVC := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey) + siVC := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) if siVC == nil { return errors.New("validators count uninitialized") } @@ -181,7 +181,7 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto } vc[len(acc.Votes)-1].Add(&vc[len(acc.Votes)-1], amount) siVC.Value = vc.Bytes() - if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, siVC); err != nil { + if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, siVC); err != nil { return err } } @@ -220,7 +220,7 @@ func (n *NEO) registerValidator(ic *interop.Context, args []stackitem.Item) stac func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error { key := makeValidatorKey(pub) - si := ic.DAO.GetStorageItem(n.Hash, key) + si := ic.DAO.GetStorageItem(n.ContractID, key) if si != nil { return errors.New("already registered") } @@ -229,7 +229,7 @@ func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey // doesn't help a lot. votes := state.NEP5BalanceState{} si.Value = votes.Bytes() - return ic.DAO.PutStorageItem(n.Hash, key, si) + return ic.DAO.PutStorageItem(n.ContractID, key, si) } func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item { @@ -263,7 +263,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public return errors.New("invalid signature") } key := makeAccountKey(h) - si := ic.DAO.GetStorageItem(n.Hash, key) + si := ic.DAO.GetStorageItem(n.ContractID, key) if si == nil { return errors.New("invalid account") } @@ -278,13 +278,13 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public // Check validators registration. var newPubs keys.PublicKeys for _, pub := range pubs { - if ic.DAO.GetStorageItem(n.Hash, makeValidatorKey(pub)) == nil { + if ic.DAO.GetStorageItem(n.ContractID, makeValidatorKey(pub)) == nil { continue } newPubs = append(newPubs, pub) } if lp, lv := len(newPubs), len(acc.Votes); lp != lv { - si := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey) + si := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { return errors.New("validators count uninitialized") } @@ -299,7 +299,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public vc[lp-1].Add(&vc[lp-1], &acc.Balance) } si.Value = vc.Bytes() - if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, si); err != nil { + if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, si); err != nil { return err } } @@ -308,14 +308,14 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public return err } si.Value = acc.Bytes() - return ic.DAO.PutStorageItem(n.Hash, key, si) + return ic.DAO.PutStorageItem(n.ContractID, key, si) } // ModifyAccountVotes modifies votes of the specified account by value (can be negative). func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int) error { for _, vote := range acc.Votes { key := makeValidatorKey(vote) - si := d.GetStorageItem(n.Hash, key) + si := d.GetStorageItem(n.ContractID, key) if si == nil { return errors.New("invalid validator") } @@ -325,7 +325,7 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *b } votes.Balance.Add(&votes.Balance, value) si.Value = votes.Bytes() - if err := d.PutStorageItem(n.Hash, key, si); err != nil { + if err := d.PutStorageItem(n.ContractID, key, si); err != nil { return err } } @@ -333,7 +333,7 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *b } func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) { - siMap, err := d.GetStorageItemsWithPrefix(n.Hash, []byte{prefixValidator}) + siMap, err := d.GetStorageItemsWithPrefix(n.ContractID, []byte{prefixValidator}) if err != nil { return nil, err } @@ -384,7 +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) { - si := d.GetStorageItem(n.Hash, validatorsCountKey) + si := d.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { return nil, errors.New("validators count uninitialized") } @@ -454,7 +454,7 @@ func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) st // GetNextBlockValidatorsInternal returns next block validators. func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { - si := d.GetStorageItem(n.Hash, nextValidatorsKey) + si := d.GetStorageItem(n.ContractID, nextValidatorsKey) if si == nil { return n.GetValidatorsInternal(bc, d) } diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index f12160de5..2d69071e2 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -108,7 +108,7 @@ func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) s } func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int { - si := ic.DAO.GetStorageItem(c.Hash, totalSupplyKey) + si := ic.DAO.GetStorageItem(c.ContractID, totalSupplyKey) if si == nil { return big.NewInt(0) } @@ -117,7 +117,7 @@ func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int { func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error { si := &state.StorageItem{Value: bigint.ToBytes(supply)} - return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si) + return ic.DAO.PutStorageItem(c.ContractID, totalSupplyKey, si) } func (c *nep5TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item { @@ -165,7 +165,7 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a } keyFrom := makeAccountKey(from) - siFrom := ic.DAO.GetStorageItem(c.Hash, keyFrom) + siFrom := ic.DAO.GetStorageItem(c.ContractID, keyFrom) if siFrom == nil { return errors.New("insufficient funds") } @@ -180,20 +180,20 @@ 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.Hash, keyFrom, siFrom); err != nil { + if err := ic.DAO.PutStorageItem(c.ContractID, keyFrom, siFrom); err != nil { return err } if !isEmpty { keyTo := makeAccountKey(to) - siTo := ic.DAO.GetStorageItem(c.Hash, keyTo) + 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 err := ic.DAO.PutStorageItem(c.Hash, keyTo, siTo); err != nil { + if err := ic.DAO.PutStorageItem(c.ContractID, keyTo, siTo); err != nil { return err } } @@ -234,14 +234,14 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount } key := makeAccountKey(h) - si := ic.DAO.GetStorageItem(c.Hash, key) + si := ic.DAO.GetStorageItem(c.ContractID, key) if si == nil { si = new(state.StorageItem) } if err := c.incBalance(ic, h, si, amount); err != nil { panic(err) } - if err := ic.DAO.PutStorageItem(c.Hash, key, si); err != nil { + if err := ic.DAO.PutStorageItem(c.ContractID, key, si); err != nil { panic(err) } diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index e2cfd12d1..cf7f39e2a 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -94,13 +94,13 @@ func (chain testChain) GetEnrollments() ([]state.Validator, error) { func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) { panic("TODO") } -func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem { +func (chain testChain) GetStorageItem(id int32, key []byte) *state.StorageItem { panic("TODO") } func (chain testChain) GetTestVM(tx *transaction.Transaction) *vm.VM { panic("TODO") } -func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { +func (chain testChain) GetStorageItems(id int32) (map[string]*state.StorageItem, error) { panic("TODO") } func (chain testChain) CurrentHeaderHash() util.Uint256 { diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 41676bcbc..613a73345 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -634,18 +634,44 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6 return d, nil } +func (s *Server) contractIDFromParam(param *request.Param) (int32, *response.Error) { + var result int32 + switch param.Type { + case request.StringT: + var err error + scriptHash, err := param.GetUint160FromHex() + if err != nil { + return 0, response.ErrInvalidParams + } + cs := s.chain.GetContractState(scriptHash) + if cs == nil { + return 0, response.ErrUnknown + } + result = cs.ID + case request.NumberT: + id, err := param.GetInt() + if err != nil { + return 0, response.ErrInvalidParams + } + result = int32(id) + default: + return 0, response.ErrInvalidParams + } + return result, nil +} + func (s *Server) getStorage(ps request.Params) (interface{}, *response.Error) { param, ok := ps.Value(0) if !ok { return nil, response.ErrInvalidParams } - - scriptHash, err := param.GetUint160FromHex() - if err != nil { - return nil, response.ErrInvalidParams + id, rErr := s.contractIDFromParam(param) + if rErr == response.ErrUnknown { + return nil, nil + } + if rErr != nil { + return nil, rErr } - - scriptHash = scriptHash.Reverse() param, ok = ps.Value(1) if !ok { @@ -657,7 +683,7 @@ func (s *Server) getStorage(ps request.Params) (interface{}, *response.Error) { return nil, response.ErrInvalidParams } - item := s.chain.GetStorageItem(scriptHash.Reverse(), key) + item := s.chain.GetStorageItem(id, key) if item == nil { return nil, nil }