diff --git a/XDCx/tradingstate/state_liquidationprice.go b/XDCx/tradingstate/state_liquidationprice.go index fe49d372fa..cb0b872112 100644 --- a/XDCx/tradingstate/state_liquidationprice.go +++ b/XDCx/tradingstate/state_liquidationprice.go @@ -136,7 +136,7 @@ func (l *liquidationPriceState) updateRoot(db Database) error { if l.dbErr != nil { return l.dbErr } - root, err := l.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := l.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil diff --git a/XDCx/tradingstate/state_orderbook.go b/XDCx/tradingstate/state_orderbook.go index bbf16f62b4..a5832543cf 100644 --- a/XDCx/tradingstate/state_orderbook.go +++ b/XDCx/tradingstate/state_orderbook.go @@ -245,7 +245,7 @@ func (te *tradingExchanges) CommitAsksTrie(db Database) error { if te.dbErr != nil { return te.dbErr } - root, err := te.asksTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := te.asksTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -307,7 +307,7 @@ func (te *tradingExchanges) CommitBidsTrie(db Database) error { if te.dbErr != nil { return te.dbErr } - root, err := te.bidsTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := te.bidsTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -783,7 +783,7 @@ func (t *tradingExchanges) CommitLiquidationPriceTrie(db Database) error { if t.dbErr != nil { return t.dbErr } - root, err := t.liquidationPriceTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := t.liquidationPriceTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList orderList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil diff --git a/XDCx/tradingstate/statedb.go b/XDCx/tradingstate/statedb.go index 52dbeccb86..73de7f7e77 100644 --- a/XDCx/tradingstate/statedb.go +++ b/XDCx/tradingstate/statedb.go @@ -589,7 +589,7 @@ func (t *TradingStateDB) Commit() (root common.Hash, err error) { } } // Write trie changes. - root, err = t.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err = t.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var exchange tradingExchangeObject if err := rlp.DecodeBytes(leaf, &exchange); err != nil { return nil diff --git a/XDCxlending/lendingstate/state_lendingbook.go b/XDCxlending/lendingstate/state_lendingbook.go index d92631bac5..49ff605732 100644 --- a/XDCxlending/lendingstate/state_lendingbook.go +++ b/XDCxlending/lendingstate/state_lendingbook.go @@ -472,7 +472,7 @@ func (le *lendingExchangeState) CommitInvestingTrie(db Database) error { if le.dbErr != nil { return le.dbErr } - root, err := le.investingTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := le.investingTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList itemList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -493,7 +493,7 @@ func (le *lendingExchangeState) CommitBorrowingTrie(db Database) error { if le.dbErr != nil { return le.dbErr } - root, err := le.borrowingTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := le.borrowingTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList itemList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil @@ -514,7 +514,7 @@ func (le *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error { if le.dbErr != nil { return le.dbErr } - root, err := le.liquidationTimeTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err := le.liquidationTimeTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var orderList itemList if err := rlp.DecodeBytes(leaf, &orderList); err != nil { return nil diff --git a/XDCxlending/lendingstate/statedb.go b/XDCxlending/lendingstate/statedb.go index 0d734f32b3..b28de7c3ed 100644 --- a/XDCxlending/lendingstate/statedb.go +++ b/XDCxlending/lendingstate/statedb.go @@ -578,7 +578,7 @@ func (ls *LendingStateDB) Commit() (root common.Hash, err error) { } } // Write trie changes. - root, err = ls.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + root, err = ls.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var exchange lendingObject if err := rlp.DecodeBytes(leaf, &exchange); err != nil { return nil diff --git a/core/state/statedb.go b/core/state/statedb.go index 85a3628b6f..35be068de9 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -840,7 +840,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { // Write the account trie changes, measuing the amount of wasted time defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now()) - return s.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error { + return s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { var account Account if err := rlp.DecodeBytes(leaf, &account); err != nil { return nil diff --git a/core/state/sync.go b/core/state/sync.go index 8a8597eee0..260429c9d3 100644 --- a/core/state/sync.go +++ b/core/state/sync.go @@ -26,17 +26,31 @@ import ( ) // NewStateSync create a new state trie download scheduler. -func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom) *trie.Sync { +func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom, onLeaf func(paths [][]byte, leaf []byte) error) *trie.Sync { + // Register the storage slot callback if the external callback is specified. + var onSlot func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error + if onLeaf != nil { + onSlot = func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error { + return onLeaf(paths, leaf) + } + } + // Register the account callback to connect the state trie and the storage + // trie belongs to the contract. var syncer *trie.Sync - callback := func(path []byte, leaf []byte, parent common.Hash) error { + onAccount := func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error { + if onLeaf != nil { + if err := onLeaf(paths, leaf); err != nil { + return err + } + } var obj Account if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil { return err } - syncer.AddSubTrie(obj.Root, path, parent, nil) - syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), path, parent) + syncer.AddSubTrie(obj.Root, hexpath, parent, onSlot) + syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), hexpath, parent) return nil } - syncer = trie.NewSync(root, database, callback, bloom) + syncer = trie.NewSync(root, database, onAccount, bloom) return syncer } diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 7ab0416995..68785a784b 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -126,7 +126,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error { // Tests that an empty state is not scheduled for syncing. func TestEmptyStateSync(t *testing.T) { - if req := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 { + if req := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New()), nil).Missing(1); len(req) != 0 { t.Errorf("content requested for empty state: %v", req) } } @@ -146,7 +146,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool) { } // Create a destination state and sync with the scheduler dstDb := rawdb.NewMemoryDatabase() - sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb)) + sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil) queue := append([]common.Hash{}, sched.Missing(count)...) for len(queue) > 0 { @@ -185,7 +185,7 @@ func TestIterativeDelayedStateSync(t *testing.T) { // Create a destination state and sync with the scheduler dstDb := rawdb.NewMemoryDatabase() - sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb)) + sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil) queue := append([]common.Hash{}, sched.Missing(0)...) for len(queue) > 0 { @@ -229,7 +229,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) { // Create a destination state and sync with the scheduler dstDb := rawdb.NewMemoryDatabase() - sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb)) + sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil) queue := make(map[common.Hash]struct{}) for _, hash := range sched.Missing(count) { @@ -276,7 +276,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) { // Create a destination state and sync with the scheduler dstDb := rawdb.NewMemoryDatabase() - sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb)) + sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil) queue := make(map[common.Hash]struct{}) for _, hash := range sched.Missing(0) { @@ -339,7 +339,7 @@ func TestIncompleteStateSync(t *testing.T) { // Create a destination state and sync with the scheduler dstDb := rawdb.NewMemoryDatabase() - sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb)) + sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil) added := []common.Hash{} queue := append([]common.Hash{}, sched.Missing(1)...) diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index d7c32793be..73ec511102 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -282,7 +282,7 @@ type stateTask struct { func newStateSync(d *Downloader, root common.Hash) *stateSync { return &stateSync{ d: d, - sched: state.NewStateSync(root, d.stateDB, trie.NewSyncBloom(1, memorydb.New())), + sched: state.NewStateSync(root, d.stateDB, trie.NewSyncBloom(1, memorydb.New()), nil), keccak: sha3.NewLegacyKeccak256(), tasks: make(map[common.Hash]*stateTask), deliver: make(chan *stateReq), diff --git a/trie/committer.go b/trie/committer.go index a64e48b2c8..f89ac6d71d 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -218,13 +218,13 @@ func (c *committer) commitLoop(db *Database) { switch n := n.(type) { case *shortNode: if child, ok := n.Val.(valueNode); ok { - c.onleaf(nil, child, hash) + c.onleaf(nil, nil, child, hash) } case *fullNode: // For children in range [0, 15], it's impossible // to contain valuenode. Only check the 17th child. if n.Children[16] != nil { - c.onleaf(nil, n.Children[16].(valueNode), hash) + c.onleaf(nil, nil, n.Children[16].(valueNode), hash) } } } diff --git a/trie/sync.go b/trie/sync.go index f6a52131cc..38ce3a1ab0 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -349,7 +349,14 @@ func (s *Sync) children(req *request, object node) ([]*request, error) { // Notify any external watcher of a new key/value Node if req.callback != nil { if node, ok := (child.node).(valueNode); ok { - if err := req.callback(req.path, node, req.hash); err != nil { + var paths [][]byte + if len(child.path) == 2*common.HashLength { + paths = append(paths, hexToKeybytes(child.path)) + } else if len(child.path) == 4*common.HashLength { + paths = append(paths, hexToKeybytes(child.path[:2*common.HashLength])) + paths = append(paths, hexToKeybytes(child.path[2*common.HashLength:])) + } + if err := req.callback(paths, child.path, node, req.hash); err != nil { return nil, err } } diff --git a/trie/trie.go b/trie/trie.go index ad5804e0f9..a1529ba4d2 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -28,9 +28,20 @@ import ( ) // LeafCallback is a callback type invoked when a trie operation reaches a leaf -// Node. It's used by state sync and commit to allow handling external references -// between account and storage tries. -type LeafCallback func(path []byte, leaf []byte, parent common.Hash) error +// node. +// +// The paths is a path tuple identifying a particular trie node either in a single +// trie (account) or a layered trie (account -> storage). Each path in the tuple +// is in the raw format(32 bytes). +// +// The hexpath is a composite hexary path identifying the trie node. All the key +// bytes are converted to the hexary nibbles and composited with the parent path +// if the trie node is in a layered trie. +// +// It's used by state sync and commit to allow handling external references +// between account and storage tries. And also it's used in the state healing +// for extracting the raw states(leaf nodes) with corresponding paths. +type LeafCallback func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error // Trie is a Merkle Patricia Trie. // The zero value is an empty trie with no database. diff --git a/trie/trie_test.go b/trie/trie_test.go index a66610eec8..6e22cb8b1d 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -477,7 +477,7 @@ func BenchmarkCommitAfterHash(b *testing.B) { benchmarkCommitAfterHash(b, nil) }) var a account - onleaf := func(path []byte, leaf []byte, parent common.Hash) error { + onleaf := func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { rlp.DecodeBytes(leaf, &a) return nil }