From 6381173293738185631fa4ec3bb8b08ce28e7164 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 23 Aug 2021 12:02:17 +0300 Subject: [PATCH] core: store statesync-related storage items under temp prefix State jump should be an atomic operation, we can't modify contract storage items state on-the-fly. Thus, store fresh items under temp prefix and replase the outdated ones after state sync is completed. Related https://github.com/nspcc-dev/neo-go/pull/2019#discussion_r693350460. --- pkg/core/blockchain.go | 24 +++++++++++++++++++++++- pkg/core/mpt/billet.go | 4 ++-- pkg/core/storage/store.go | 19 ++++++++++++------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 3aa37943d..2e844950c 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -35,6 +35,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "go.uber.org/zap" @@ -43,7 +44,7 @@ import ( // Tuning parameters. const ( headerBatchCount = 2000 - version = "0.1.2" + version = "0.1.3" defaultInitialGAS = 52000000_00000000 defaultMemPoolSize = 50000 @@ -451,6 +452,27 @@ func (bc *Blockchain) JumpToState(module blockchainer.StateSync) error { return fmt.Errorf("can't perform MPT jump to height %d: %w", p, err) } + b := bc.dao.Store.Batch() + bc.dao.Store.Seek([]byte{byte(storage.STStorage)}, func(k, _ []byte) { + // Must copy here, #1468. + key := slice.Copy(k) + b.Delete(key) + }) + bc.dao.Store.Seek([]byte{byte(storage.STTempStorage)}, func(k, v []byte) { + // Must copy here, #1468. + oldKey := slice.Copy(k) + b.Delete(oldKey) + key := make([]byte, len(k)) + key[0] = byte(storage.STStorage) + copy(key[1:], k[1:]) + value := slice.Copy(v) + b.Put(key, value) + }) + err = bc.dao.Store.PutBatch(b) + if err != nil { + return fmt.Errorf("failed to replace outdated contract storage items with the fresh ones: %w", err) + } + err = bc.contracts.NEO.InitializeCache(bc, bc.dao) if err != nil { return fmt.Errorf("can't init cache for NEO native contract: %w", err) diff --git a/pkg/core/mpt/billet.go b/pkg/core/mpt/billet.go index 843a746ee..b2e19c3c8 100644 --- a/pkg/core/mpt/billet.go +++ b/pkg/core/mpt/billet.go @@ -62,9 +62,9 @@ func (b *Billet) RestoreHashNode(path []byte, node Node) error { } b.root = r - // If it's a leaf, then put into contract storage. + // If it's a leaf, then put into temporary contract storage. if leaf, ok := node.(*LeafNode); ok { - k := append([]byte{byte(storage.STStorage)}, fromNibbles(path)...) + k := append([]byte{byte(storage.STTempStorage)}, fromNibbles(path)...) _ = b.Store.Put(k, leaf.value) } return nil diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index dd2376c63..26f5f5133 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -8,13 +8,18 @@ import ( // KeyPrefix constants. const ( - DataBlock KeyPrefix = 0x01 - DataTransaction KeyPrefix = 0x02 - DataMPT KeyPrefix = 0x03 - STAccount KeyPrefix = 0x40 - STNotification KeyPrefix = 0x4d - STContractID KeyPrefix = 0x51 - STStorage KeyPrefix = 0x70 + DataBlock KeyPrefix = 0x01 + DataTransaction KeyPrefix = 0x02 + DataMPT KeyPrefix = 0x03 + STAccount KeyPrefix = 0x40 + STNotification KeyPrefix = 0x4d + STContractID KeyPrefix = 0x51 + STStorage KeyPrefix = 0x70 + // STTempStorage is used to store contract storage items during state sync process + // in order not to mess up the previous state which has its own items stored by + // STStorage prefix. Once state exchange process is completed, all items with + // STStorage prefix will be replaced with STTempStorage-prefixed ones. + STTempStorage KeyPrefix = 0x71 STNEP17Transfers KeyPrefix = 0x72 STNEP17TransferInfo KeyPrefix = 0x73 IXHeaderHashList KeyPrefix = 0x80