From bfddf9b3f66043c4f2e4a49591201670c9f043f5 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 11 Oct 2019 14:08:21 +0300 Subject: [PATCH] core: implement StorageItem for future interops --- pkg/core/blockchain.go | 37 ++++++++++++++++++++ pkg/core/blockchainer.go | 2 ++ pkg/core/storage_item.go | 64 +++++++++++++++++++++++++++++++++++ pkg/core/storage_item_test.go | 26 ++++++++++++++ pkg/network/helper_test.go | 6 ++++ 5 files changed, 135 insertions(+) create mode 100644 pkg/core/storage_item.go create mode 100644 pkg/core/storage_item_test.go diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 8e30350e9..d8051e9c0 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -512,6 +512,43 @@ func getTransactionFromStore(s storage.Store, hash util.Uint256) (*transaction.T return tx, height, nil } +// GetStorageItem returns an item from storage. +func (bc *Blockchain) GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem { + sItem := getStorageItemFromStore(bc.memStore, scripthash, key) + if sItem == nil { + sItem = getStorageItemFromStore(bc.Store, scripthash, key) + } + return sItem +} + +// GetStorageItems returns all storage items for a given scripthash. +func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error) { + var siMap = make(map[string]*StorageItem) + var err error + + saveToMap := func(k, v []byte) { + if err != nil { + return + } + r := io.NewBinReaderFromBuf(v) + si := &StorageItem{} + si.DecodeBinary(r) + if r.Err != nil { + err = r.Err + return + } + + // Cut prefix and hash. + siMap[string(k[21:])] = si + } + bc.memStore.Seek(storage.AppendPrefix(storage.STStorage, hash.BytesReverse()), saveToMap) + bc.Store.Seek(storage.AppendPrefix(storage.STStorage, hash.BytesReverse()), saveToMap) + if err != nil { + return nil, err + } + return siMap, nil +} + // GetBlock returns a Block by the given hash. func (bc *Blockchain) GetBlock(hash util.Uint256) (*Block, error) { block, err := getBlockFromStore(bc.memStore, hash) diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index 53f1d6400..85724a4cd 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -23,6 +23,8 @@ type Blockchainer interface { HasTransaction(util.Uint256) bool GetAssetState(util.Uint256) *AssetState GetAccountState(util.Uint160) *AccountState + GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem + GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) References(t *transaction.Transaction) map[transaction.Input]*transaction.Output Feer // fee interface diff --git a/pkg/core/storage_item.go b/pkg/core/storage_item.go new file mode 100644 index 000000000..2e6166a8b --- /dev/null +++ b/pkg/core/storage_item.go @@ -0,0 +1,64 @@ +package core + +import ( + "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/util" +) + +// StorageItem is the value to be stored with read-only flag. +type StorageItem struct { + Value []byte + IsConst bool +} + +// 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.BytesReverse(), key...)) +} + +// getStorageItemFromStore returns StorageItem if it exists in the given Store. +func getStorageItemFromStore(s storage.Store, scripthash util.Uint160, key []byte) *StorageItem { + b, err := s.Get(makeStorageItemKey(scripthash, key)) + if err != nil { + return nil + } + r := io.NewBinReaderFromBuf(b) + + si := &StorageItem{} + si.DecodeBinary(r) + if r.Err != nil { + return nil + } + + return si +} + +// putStorageItemIntoStore puts given StorageItem for given script with given +// key into the given Store. +func putStorageItemIntoStore(s storage.Store, scripthash util.Uint160, key []byte, si *StorageItem) error { + buf := io.NewBufBinWriter() + si.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err + } + return s.Put(makeStorageItemKey(scripthash, key), buf.Bytes()) +} + +// deleteStorageItemInStore drops storage item for the given script with the +// given key from the Store. +func deleteStorageItemInStore(s storage.Store, scripthash util.Uint160, key []byte) error { + return s.Delete(makeStorageItemKey(scripthash, key)) +} + +// EncodeBinary implements Serializable interface. +func (si *StorageItem) EncodeBinary(w *io.BinWriter) { + w.WriteBytes(si.Value) + w.WriteLE(si.IsConst) +} + +// DecodeBinary implements Serializable interface. +func (si *StorageItem) DecodeBinary(r *io.BinReader) { + si.Value = r.ReadBytes() + r.ReadLE(&si.IsConst) +} diff --git a/pkg/core/storage_item_test.go b/pkg/core/storage_item_test.go new file mode 100644 index 000000000..d555cdd21 --- /dev/null +++ b/pkg/core/storage_item_test.go @@ -0,0 +1,26 @@ +package core + +import ( + "testing" + + "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestPutGetDeleteStorageItem(t *testing.T) { + s := storage.NewMemoryStore() + si := &StorageItem{ + Value: []byte("smth"), + } + key := []byte("key") + cHash, err := util.Uint160DecodeBytes([]byte("abcdefghijklmnopqrst")) + assert.Nil(t, err) + assert.NoError(t, putStorageItemIntoStore(s, cHash, key, si)) + siRead := getStorageItemFromStore(s, cHash, key) + assert.NotNil(t, siRead) + assert.Equal(t, si, siRead) + assert.NoError(t, deleteStorageItemInStore(s, cHash, key)) + siRead2 := getStorageItemFromStore(s, cHash, key) + assert.Nil(t, siRead2) +} diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 09fafb87d..0aeec48d9 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -69,6 +69,12 @@ func (chain testChain) GetAssetState(util.Uint256) *core.AssetState { func (chain testChain) GetAccountState(util.Uint160) *core.AccountState { panic("TODO") } +func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *core.StorageItem { + panic("TODO") +} +func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*core.StorageItem, error) { + panic("TODO") +} func (chain testChain) CurrentHeaderHash() util.Uint256 { return util.Uint256{} }