-
Notifications
You must be signed in to change notification settings - Fork 20.5k
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
trie, les, tests, core: implement trie tracer #24403
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import ( | |
"sync" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/rawdb" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/ethereum/go-ethereum/log" | ||
|
@@ -62,17 +63,32 @@ type LeafCallback func(paths [][]byte, hexpath []byte, leaf []byte, parent commo | |
type Trie struct { | ||
db *Database | ||
root node | ||
// Keep track of the number leafs which have been inserted since the last | ||
|
||
// Keep track of the number leaves which have been inserted since the last | ||
// hashing operation. This number will not directly map to the number of | ||
// actually unhashed nodes | ||
unhashed int | ||
|
||
// tracer is the state diff tracer can be used to track newly added/deleted | ||
// trie node. It will be reset after each commit operation. | ||
tracer *tracer | ||
} | ||
|
||
// newFlag returns the cache flag value for a newly created node. | ||
func (t *Trie) newFlag() nodeFlag { | ||
return nodeFlag{dirty: true} | ||
} | ||
|
||
// Copy returns a copy of Trie. | ||
func (t *Trie) Copy() *Trie { | ||
return &Trie{ | ||
db: t.db, | ||
root: t.root, | ||
unhashed: t.unhashed, | ||
tracer: t.tracer.copy(), | ||
} | ||
} | ||
|
||
// New creates a trie with an existing root node from db. | ||
// | ||
// If root is the zero hash or the sha3 hash of an empty string, the | ||
|
@@ -85,6 +101,7 @@ func New(root common.Hash, db *Database) (*Trie, error) { | |
} | ||
trie := &Trie{ | ||
db: db, | ||
//tracer: newTracer(), | ||
} | ||
if root != (common.Hash{}) && root != emptyRoot { | ||
rootnode, err := trie.resolveHash(root[:], nil) | ||
|
@@ -96,6 +113,16 @@ func New(root common.Hash, db *Database) (*Trie, error) { | |
return trie, nil | ||
} | ||
|
||
// newWithRootNode initializes the trie with the given root node. | ||
// It's only used by range prover. | ||
func newWithRootNode(root node) *Trie { | ||
return &Trie{ | ||
root: root, | ||
//tracer: newTracer(), | ||
db: NewDatabase(rawdb.NewMemoryDatabase()), | ||
} | ||
} | ||
|
||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at | ||
// the key after the given start key. | ||
func (t *Trie) NodeIterator(start []byte) NodeIterator { | ||
|
@@ -317,7 +344,12 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error | |
if matchlen == 0 { | ||
return true, branch, nil | ||
} | ||
// Otherwise, replace it with a short node leading up to the branch. | ||
// New branch node is created as a child of the original short node. | ||
// Track the newly inserted node in the tracer. The node identifier | ||
// passed is the path from the root node. | ||
t.tracer.onInsert(append(prefix, key[:matchlen]...)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how does this not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought that this might be costly -- even if the tracer is |
||
|
||
// Replace it with a short node leading up to the branch. | ||
return true, &shortNode{key[:matchlen], branch, t.newFlag()}, nil | ||
|
||
case *fullNode: | ||
|
@@ -331,6 +363,11 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error | |
return true, n, nil | ||
|
||
case nil: | ||
// New short node is created and track it in the tracer. The node identifier | ||
// passed is the path from the root node. Note the valueNode won't be tracked | ||
// since it's always embedded in its parent. | ||
t.tracer.onInsert(prefix) | ||
|
||
return true, &shortNode{key, value, t.newFlag()}, nil | ||
|
||
case hashNode: | ||
|
@@ -383,6 +420,11 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { | |
return false, n, nil // don't replace n on mismatch | ||
} | ||
if matchlen == len(key) { | ||
// The matched short node is deleted entirely and track | ||
// it in the deletion set. The same the valueNode doesn't | ||
// need to be tracked at all since it's always embedded. | ||
t.tracer.onDelete(prefix) | ||
|
||
return true, nil, nil // remove n entirely for whole matches | ||
} | ||
// The key is longer than n.Key. Remove the remaining suffix | ||
|
@@ -395,6 +437,10 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { | |
} | ||
switch child := child.(type) { | ||
case *shortNode: | ||
// The child shortNode is merged into its parent, track | ||
// is deleted as well. | ||
t.tracer.onDelete(append(prefix, n.Key...)) | ||
|
||
// Deleting from the subtrie reduced it to another | ||
// short node. Merge the nodes to avoid creating a | ||
// shortNode{..., shortNode{...}}. Use concat (which | ||
|
@@ -456,6 +502,11 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { | |
return false, nil, err | ||
} | ||
if cnode, ok := cnode.(*shortNode); ok { | ||
// Replace the entire full node with the short node. | ||
// Mark the original short node as deleted since the | ||
// value is embedded into the parent now. | ||
t.tracer.onDelete(append(prefix, byte(pos))) | ||
|
||
k := append([]byte{byte(pos)}, cnode.Key...) | ||
return true, &shortNode{k, cnode.Val, t.newFlag()}, nil | ||
} | ||
|
@@ -537,6 +588,8 @@ func (t *Trie) Commit(onleaf LeafCallback) (common.Hash, int, error) { | |
if t.db == nil { | ||
panic("commit called on trie with nil database") | ||
} | ||
defer t.tracer.reset() | ||
|
||
if t.root == nil { | ||
return emptyRoot, 0, nil | ||
} | ||
|
@@ -595,4 +648,5 @@ func (t *Trie) hashRoot() (node, node, error) { | |
func (t *Trie) Reset() { | ||
t.root = nil | ||
t.unhashed = 0 | ||
t.tracer.reset() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you make these changes? Old code seems to work fine, without instantiating new databases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original initialization
new(trie.Trie)
will create the trie structure but leave all pointers as nil.Right now it's
db
andtracer
. Although in this case both of them are not used, but it's still very dangerous to leave pointers as nil.I did a lots of this kind changes to ensure all the created trie instances are initialized properly.