Merge pull request #1077 from nspcc-dev/neo3/smartcontract/id

core: store smartcontract items by id
This commit is contained in:
Roman Khimov 2020-06-20 00:12:07 +03:00 committed by GitHub
commit 36a65e3847
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 168 additions and 140 deletions

View file

@ -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.

View file

@ -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

View file

@ -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)
}

View file

@ -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.

View file

@ -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)
}

View file

@ -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 err != nil {
return err
}
for k, v := range siMap {
v.IsConst = false
err = ic.DAO.PutStorageItem(contract.ScriptHash(), []byte(k), v)
if !newcontract.HasStorage() {
siMap, err := ic.DAO.GetStorageItems(contract.ID)
if err != nil {
return err
}
if len(siMap) != 0 {
return errors.New("old contract shouldn't have storage")
}
}
v.Estack().PushVal(stackitem.NewInterop(contract))

View file

@ -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)

View file

@ -28,10 +28,10 @@ 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
ID int32
ReadOnly bool
}
@ -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,8 +296,15 @@ 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 errors.New("contract is not allowed to use storage")
}
sc := &StorageContext{
ScriptHash: v.GetCurrentScriptHash(),
ID: contract.ID,
ReadOnly: false,
}
v.Estack().PushVal(stackitem.NewInterop(sc))
@ -325,8 +313,15 @@ 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(),
ID: contract.ID,
ReadOnly: true,
}
v.Estack().PushVal(stackitem.NewInterop(sc))
@ -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,7 +389,7 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
}
if !stc.ReadOnly {
stx := &StorageContext{
ScriptHash: stc.ScriptHash,
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

View file

@ -34,7 +34,7 @@ type keyWithVotes struct {
const (
neoSyscallName = "Neo.Native.Tokens.NEO"
neoContractID = -2
neoContractID = -1
// NEOTotalSupply is the total amount of NEO in the system.
NEOTotalSupply = 100000000
// prefixValidator is a prefix used to store validator's data.
@ -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)
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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
}