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.
This commit is contained in:
Anna Shaleva 2021-08-23 12:02:17 +03:00
parent 51f405471e
commit 6381173293
3 changed files with 37 additions and 10 deletions

View file

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

View file

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

View file

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