forked from TrueCloudLab/neoneo-go
Merge pull request #1077 from nspcc-dev/neo3/smartcontract/id
core: store smartcontract items by id
This commit is contained in:
commit
36a65e3847
12 changed files with 168 additions and 140 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue