Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVM-864 Snapshot writing error not handled #1993

Merged
merged 3 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading