core,dao: save contract metadata on migration

After contract is migrated there is no way to retrieve it's state.
This commit implements some metadata for NEP5 contracts, so that
values important for diplaying transfer log aren't lost.
This commit is contained in:
Evgenii Stratonikov 2020-07-21 13:06:33 +03:00
parent 7bdbfbad19
commit 7cd1bca1e1
7 changed files with 67 additions and 1 deletions

View file

@ -994,6 +994,12 @@ func (bc *Blockchain) GetNEP5Balances(acc util.Uint160) *state.NEP5Balances {
return bs
}
// GetNEP5Metadata returns NEP5 metadata for the contract h.
// Note: it is currently saved only for migrated contracts.
func (bc *Blockchain) GetNEP5Metadata(h util.Uint160) (*state.NEP5Metadata, error) {
return bc.dao.GetNEP5Metadata(h)
}
// LastBatch returns last persisted storage batch.
func (bc *Blockchain) LastBatch() *storage.MemBatch {
return bc.lastBatch

View file

@ -35,6 +35,7 @@ type Blockchainer interface {
GetAssetState(util.Uint256) *state.Asset
GetAccountState(util.Uint160) *state.Account
GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
GetNEP5Metadata(util.Uint160) (*state.NEP5Metadata, error)
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
GetNEP5Balances(util.Uint160) *state.NEP5Balances
GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error)

View file

@ -35,6 +35,7 @@ type DAO interface {
GetCurrentStateRootHeight() (uint32, error)
GetHeaderHashes() ([]util.Uint256, error)
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
GetNEP5Metadata(h util.Uint160) (*state.NEP5Metadata, error)
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
GetStateRoot(height uint32) (*state.MPTRootState, error)
PutStateRoot(root *state.MPTRootState) error
@ -58,6 +59,7 @@ type DAO interface {
PutContractState(cs *state.Contract) error
PutCurrentHeader(hashAndIndex []byte) error
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
PutNEP5Metadata(h util.Uint160, meta *state.NEP5Metadata) error
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
@ -215,6 +217,22 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
return dao.Store.Delete(key)
}
// GetNEP5Metadata returns saved NEP5 metadata for the contract h.
func (dao *Simple) GetNEP5Metadata(h util.Uint160) (*state.NEP5Metadata, error) {
key := storage.AppendPrefix(storage.STMigration, h.BytesBE())
m := new(state.NEP5Metadata)
if err := dao.GetAndDecode(m, key); err != nil {
return nil, err
}
return m, nil
}
// PutNEP5Metadata saves NEP5 metadata for the contract h.
func (dao *Simple) PutNEP5Metadata(h util.Uint160, m *state.NEP5Metadata) error {
key := storage.AppendPrefix(storage.STMigration, h.BytesBE())
return dao.Put(m, key)
}
// -- end contracts.
// -- start nep5 balances.

View file

@ -12,10 +12,12 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
gherr "github.com/pkg/errors"
)
@ -583,8 +585,8 @@ func (ic *interopContext) contractMigrate(v *vm.VM) error {
if err != nil {
return err
}
hash := getContextScriptHash(v, 0)
if contract.HasStorage() {
hash := getContextScriptHash(v, 0)
siMap, err := ic.dao.GetStorageItems(hash, nil)
if err != nil {
return err
@ -598,6 +600,26 @@ func (ic *interopContext) contractMigrate(v *vm.VM) error {
}
}
ic.dao.MigrateNEP5Balances(hash, contract.ScriptHash())
// save NEP5 metadata if any
v := ic.bc.GetTestVM()
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, hash, "decimals")
v.SetGasLimit(ic.bc.GetConfig().FreeGasLimit)
v.Load(w.Bytes())
if err := v.Run(); err == nil && v.Estack().Len() == 1 {
res := v.Estack().Pop().Item().ToContractParameter(map[vm.StackItem]bool{})
d := int64(-1)
switch res.Type {
case smartcontract.IntegerType:
d = res.Value.(int64)
case smartcontract.ByteArrayType:
d = emit.BytesToInt(res.Value.([]byte)).Int64()
}
if d >= 0 {
ic.dao.PutNEP5Metadata(hash, &state.NEP5Metadata{Decimals: d})
}
}
}
}
v.Estack().PushVal(vm.NewInteropItem(contract))

View file

@ -49,6 +49,11 @@ type NEP5Balances struct {
NextTransferBatch uint32
}
// NEP5Metadata is a metadata for NEP5 contracts.
type NEP5Metadata struct {
Decimals int64
}
// NewNEP5Balances returns new NEP5Balances.
func NewNEP5Balances() *NEP5Balances {
return &NEP5Balances{
@ -81,6 +86,16 @@ func (bs *NEP5Balances) EncodeBinary(w *io.BinWriter) {
}
}
// DecodeBinary implements io.Serializable interface.
func (bs *NEP5Metadata) DecodeBinary(r *io.BinReader) {
bs.Decimals = int64(r.ReadU64LE())
}
// EncodeBinary implements io.Serializable interface.
func (bs *NEP5Metadata) EncodeBinary(w *io.BinWriter) {
w.WriteU64LE(uint64(bs.Decimals))
}
// Append appends single transfer to a log.
func (lg *NEP5TransferLog) Append(tr *NEP5Transfer) error {
w := io.NewBufBinWriter()

View file

@ -17,6 +17,7 @@ const (
STAsset KeyPrefix = 0x4c
STNotification KeyPrefix = 0x4d
STContract KeyPrefix = 0x50
STMigration KeyPrefix = 0x51
STStorage KeyPrefix = 0x70
STNEP5Transfers KeyPrefix = 0x72
STNEP5Balances KeyPrefix = 0x73

View file

@ -93,6 +93,9 @@ func (chain testChain) GetAssetState(util.Uint256) *state.Asset {
func (chain testChain) GetAccountState(util.Uint160) *state.Account {
panic("TODO")
}
func (chain testChain) GetNEP5Metadata(util.Uint160) (*state.NEP5Metadata, error) {
panic("TODO")
}
func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog {
panic("TODO")
}