Skip to content

Commit

Permalink
EVM-864 Snapshot writing error not handled (0xPolygon#1993)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorcrevar authored Oct 23, 2023
1 parent 38fa45b commit 2966d12
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 80 deletions.
10 changes: 8 additions & 2 deletions state/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ func (e *Executor) WriteGenesis(
return types.Hash{}, err
}

_, root := snap.Commit(objs)
_, root, err := snap.Commit(objs)
if err != nil {
return types.Hash{}, err
}

return types.BytesToHash(root), nil
}
Expand Down Expand Up @@ -396,7 +399,10 @@ func (t *Transition) Commit() (Snapshot, types.Hash, error) {
return nil, types.ZeroHash, err
}

s2, root := t.snap.Commit(objs)
s2, root, err := t.snap.Commit(objs)
if err != nil {
return nil, types.ZeroHash, err
}

return s2, types.BytesToHash(root), nil
}
Expand Down
40 changes: 26 additions & 14 deletions state/immutable-trie/copy_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import (
var emptyCodeHash = crypto.Keccak256(nil)

func getCustomNode(hash []byte, storage Storage) (Node, []byte, error) {
data, ok := storage.Get(hash)
if !ok {
return nil, nil, nil
data, ok, err := storage.Get(hash)
if err != nil || !ok {
return nil, nil, err
}

// NOTE. We dont need to make copies of the bytes because the nodes
Expand All @@ -42,32 +42,42 @@ func getCustomNode(hash []byte, storage Storage) (Node, []byte, error) {
}

func CopyTrie(nodeHash []byte, storage Storage, newStorage Storage, agg []byte, isStorage bool) error {
batchWriter := newStorage.Batch()

if err := copyTrieHash(nodeHash, storage, batchWriter, agg, isStorage); err != nil {
return err
}

return batchWriter.Write()
}

func copyTrieHash(nodeHash []byte, storage Storage, batchWriter Batch, agg []byte, isStorage bool) error {
node, data, err := getCustomNode(nodeHash, storage)
if err != nil {
return err
}

//copy whole bytes of nodes
newStorage.Put(nodeHash, data)
batchWriter.Put(nodeHash, data)

return copyTrie(node, storage, newStorage, agg, isStorage)
return copyTrieNode(node, storage, batchWriter, agg, isStorage)
}

func copyTrie(node Node, storage Storage, newStorage Storage, agg []byte, isStorage bool) error {
func copyTrieNode(node Node, storage Storage, batchWriter Batch, agg []byte, isStorage bool) error {
switch n := node.(type) {
case nil:
return nil
case *FullNode:
if len(n.hash) > 0 {
return CopyTrie(n.hash, storage, newStorage, agg, isStorage)
return copyTrieHash(n.hash, storage, batchWriter, agg, isStorage)
}

for i := range n.children {
if n.children[i] == nil {
continue
}

err := copyTrie(n.children[i], storage, newStorage, append(agg, uint8(i)), isStorage)
err := copyTrieNode(n.children[i], storage, batchWriter, append(agg, uint8(i)), isStorage)
if err != nil {
return err
}
Expand All @@ -76,7 +86,7 @@ func copyTrie(node Node, storage Storage, newStorage Storage, agg []byte, isStor
case *ValueNode:
//if node represens stored value, then we need to copy it
if n.hash {
return CopyTrie(n.buf, storage, newStorage, agg, isStorage)
return copyTrieHash(n.buf, storage, batchWriter, agg, isStorage)
}

if !isStorage {
Expand All @@ -85,26 +95,28 @@ func copyTrie(node Node, storage Storage, newStorage Storage, agg []byte, isStor
return fmt.Errorf("can't parse account %s: %w", hex.EncodeToString(encodeCompact(agg)), err)
} else {
if account.CodeHash != nil && bytes.Equal(account.CodeHash, emptyCodeHash) == false {
code, ok := storage.GetCode(types.BytesToHash(account.CodeHash))
hash := types.BytesToHash(account.CodeHash)

code, ok := storage.GetCode(hash)
if ok {
newStorage.SetCode(types.BytesToHash(account.CodeHash), code)
batchWriter.Put(GetCodeKey(hash), code)
} else {
return fmt.Errorf("can't find code %s", hex.EncodeToString(account.CodeHash))
}
}

if account.Root != types.EmptyRootHash {
return CopyTrie(account.Root[:], storage, newStorage, nil, true)
return copyTrieHash(account.Root[:], storage, batchWriter, nil, true)
}
}
}

case *ShortNode:
if len(n.hash) > 0 {
return CopyTrie(n.hash, storage, newStorage, agg, isStorage)
return copyTrieHash(n.hash, storage, batchWriter, agg, isStorage)
}

return copyTrie(n.child, storage, newStorage, append(agg, n.key...), isStorage)
return copyTrieNode(n.child, storage, batchWriter, append(agg, n.key...), isStorage)
}

return nil
Expand Down
18 changes: 12 additions & 6 deletions state/immutable-trie/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package itrie

import (
"bytes"
"fmt"

"github.com/0xPolygon/polygon-edge/crypto"
"github.com/0xPolygon/polygon-edge/state"
Expand Down Expand Up @@ -73,7 +74,7 @@ func (s *Snapshot) GetCode(hash types.Hash) ([]byte, bool) {
return s.state.GetCode(hash)
}

func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) {
func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte, error) {
batch := s.state.storage.Batch()

tt := s.trie.Txn(s.state.storage)
Expand All @@ -96,7 +97,7 @@ func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) {
if len(obj.Storage) != 0 {
trie, err := s.state.newTrieAt(obj.Root)
if err != nil {
panic(err) //nolint:gocritic
return nil, types.ZeroHash[:], fmt.Errorf("snapshot commit failed to create trie: %w", err)
}

localTxn := trie.Txn(s.state.storage)
Expand All @@ -122,7 +123,7 @@ func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) {
}

if obj.DirtyCode {
s.state.SetCode(obj.CodeHash, obj.Code)
batch.Put(GetCodeKey(obj.CodeHash), obj.Code)
}

vv := account.MarshalWith(arena)
Expand All @@ -133,14 +134,19 @@ func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) {
}
}

root, _ := tt.Hash()
root, err := tt.Hash()
if err != nil {
return nil, types.ZeroHash[:], fmt.Errorf("snapshot commit can not retrieve hash: %w", err)
}

nTrie := tt.Commit()

// Write all the entries to db
batch.Write()
if err := batch.Write(); err != nil {
return nil, types.ZeroHash[:], fmt.Errorf("snapshot commit db write error: %w", err)
}

s.state.AddState(types.BytesToHash(root), nTrie)

return &Snapshot{trie: nTrie, state: s.state}, root
return &Snapshot{trie: nTrie, state: s.state}, root, nil
}
4 changes: 2 additions & 2 deletions state/immutable-trie/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func (s *State) newTrie() *Trie {
return NewTrie()
}

func (s *State) SetCode(hash types.Hash, code []byte) {
s.storage.SetCode(hash, code)
func (s *State) SetCode(hash types.Hash, code []byte) error {
return s.storage.SetCode(hash, code)
}

func (s *State) GetCode(hash types.Hash) ([]byte, bool) {
Expand Down
85 changes: 50 additions & 35 deletions state/immutable-trie/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,25 @@ var parserPool fastrlp.ParserPool
var (
// codePrefix is the code prefix for leveldb
codePrefix = []byte("code")

// leveldb not found error message
levelDBNotFoundMsg = "leveldb: not found"
)

// Batch is batch write interface
type Batch interface {
// Put puts key and value into batch. It can not return error because actual writing is done with Write method
Put(k, v []byte)
Write()
// Write writes all the key values pair previosly putted with Put method to the database
Write() error
}

// Storage stores the trie
type Storage interface {
Put(k, v []byte)
Get(k []byte) ([]byte, bool)
Put(k, v []byte) error
Get(k []byte) ([]byte, bool, error)
Batch() Batch
SetCode(hash types.Hash, code []byte)
SetCode(hash types.Hash, code []byte) error
GetCode(hash types.Hash) ([]byte, bool)

Close() error
Expand All @@ -49,37 +55,42 @@ func (b *KVBatch) Put(k, v []byte) {
b.batch.Put(k, v)
}

func (b *KVBatch) Write() {
_ = b.db.Write(b.batch, nil)
func (b *KVBatch) Write() error {
return b.db.Write(b.batch, nil)
}

func (kv *KVStorage) SetCode(hash types.Hash, code []byte) {
kv.Put(append(codePrefix, hash.Bytes()...), code)
func (kv *KVStorage) SetCode(hash types.Hash, code []byte) error {
return kv.Put(GetCodeKey(hash), code)
}

func (kv *KVStorage) GetCode(hash types.Hash) ([]byte, bool) {
return kv.Get(append(codePrefix, hash.Bytes()...))
res, ok, err := kv.Get(GetCodeKey(hash))
if err != nil {
return nil, false
}

return res, ok
}

func (kv *KVStorage) Batch() Batch {
return &KVBatch{db: kv.db, batch: &leveldb.Batch{}}
}

func (kv *KVStorage) Put(k, v []byte) {
_ = kv.db.Put(k, v, nil)
func (kv *KVStorage) Put(k, v []byte) error {
return kv.db.Put(k, v, nil)
}

func (kv *KVStorage) Get(k []byte) ([]byte, bool) {
func (kv *KVStorage) Get(k []byte) ([]byte, bool, error) {
data, err := kv.db.Get(k, nil)
if err != nil {
if err.Error() == "leveldb: not found" {
return nil, false
} else {
panic(err) //nolint:gocritic
if err.Error() == levelDBNotFoundMsg {
return nil, false, nil
}

return nil, false, err
}

return data, true
return data, true, nil
}

func (kv *KVStorage) Close() error {
Expand Down Expand Up @@ -111,41 +122,40 @@ func NewMemoryStorage() Storage {
return &memStorage{db: map[string][]byte{}, code: map[string][]byte{}, l: new(sync.Mutex)}
}

func (m *memStorage) Put(p []byte, v []byte) {
func (m *memStorage) Put(p []byte, v []byte) error {
m.l.Lock()
defer m.l.Unlock()

buf := make([]byte, len(v))
copy(buf[:], v[:])
m.db[hex.EncodeToHex(p)] = buf

return nil
}

func (m *memStorage) Get(p []byte) ([]byte, bool) {
func (m *memStorage) Get(p []byte) ([]byte, bool, error) {
m.l.Lock()
defer m.l.Unlock()

v, ok := m.db[hex.EncodeToHex(p)]
if !ok {
return []byte{}, false
return []byte{}, false, nil
}

return v, true
return v, true, nil
}

func (m *memStorage) SetCode(hash types.Hash, code []byte) {
m.l.Lock()
defer m.l.Unlock()

m.code[hash.String()] = code
func (m *memStorage) SetCode(hash types.Hash, code []byte) error {
return m.Put(append(codePrefix, hash.Bytes()...), code)
}

func (m *memStorage) GetCode(hash types.Hash) ([]byte, bool) {
m.l.Lock()
defer m.l.Unlock()

code, ok := m.code[hash.String()]
res, ok, err := m.Get(GetCodeKey(hash))
if err != nil {
return nil, false
}

return code, ok
return res, ok
}

func (m *memStorage) Batch() Batch {
Expand All @@ -165,14 +175,15 @@ func (m *memBatch) Put(p, v []byte) {
(*m.db)[hex.EncodeToHex(p)] = buf
}

func (m *memBatch) Write() {
func (m *memBatch) Write() error {
return nil
}

// GetNode retrieves a node from storage
func GetNode(root []byte, storage Storage) (Node, bool, error) {
data, ok := storage.Get(root)
if !ok || len(data) == 0 {
return nil, false, nil
data, ok, err := storage.Get(root)
if err != nil || !ok || len(data) == 0 {
return nil, false, err
}

// NOTE. We dont need to make copies of the bytes because the nodes
Expand Down Expand Up @@ -263,3 +274,7 @@ func decodeNode(v *fastrlp.Value, s Storage) (Node, error) {

return nil, fmt.Errorf("node has incorrect number of leafs")
}

func GetCodeKey(hash types.Hash) []byte {
return append(codePrefix, hash.Bytes()...)
}
2 changes: 1 addition & 1 deletion state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type State interface {
type Snapshot interface {
readSnapshot

Commit(objs []*Object) (Snapshot, []byte)
Commit(objs []*Object) (Snapshot, []byte, error)
}

// Account is the account reference in the ethereum state
Expand Down
Loading

0 comments on commit 2966d12

Please sign in to comment.