trie: fix the double reference fix (i.e. roots *can* be double refed)

This commit is contained in:
Péter Szilágyi 2018-01-24 17:30:17 +02:00
parent 4315480e09
commit b2d8f148d2
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D

View file

@ -61,9 +61,9 @@ type Database struct {
// cachedNode is all the information we know about a single cached node in the // cachedNode is all the information we know about a single cached node in the
// memory database write layer. // memory database write layer.
type cachedNode struct { type cachedNode struct {
blob []byte // Cached data block of the trie node blob []byte // Cached data block of the trie node
parents int // Number of live nodes referencing this one parents int // Number of live nodes referencing this one
children map[common.Hash]struct{} // Children referenced by this nodes children map[common.Hash]int // Children referenced by this nodes
} }
// NewDatabase creates a new trie database to store ephemeral trie content before // NewDatabase creates a new trie database to store ephemeral trie content before
@ -72,7 +72,7 @@ func NewDatabase(diskdb ethdb.Database) *Database {
return &Database{ return &Database{
diskdb: diskdb, diskdb: diskdb,
nodes: map[common.Hash]*cachedNode{ nodes: map[common.Hash]*cachedNode{
common.Hash{}: {children: make(map[common.Hash]struct{})}, common.Hash{}: {children: make(map[common.Hash]int)},
}, },
preimages: make(map[common.Hash][]byte), preimages: make(map[common.Hash][]byte),
} }
@ -99,7 +99,7 @@ func (db *Database) insert(hash common.Hash, blob []byte) {
} }
db.nodes[hash] = &cachedNode{ db.nodes[hash] = &cachedNode{
blob: common.CopyBytes(blob), blob: common.CopyBytes(blob),
children: make(map[common.Hash]struct{}), children: make(map[common.Hash]int),
} }
db.size += common.StorageSize(common.HashLength + len(blob)) db.size += common.StorageSize(common.HashLength + len(blob))
} }
@ -185,12 +185,12 @@ func (db *Database) reference(child common.Hash, parent common.Hash) {
if !ok { if !ok {
return return
} }
// If the reference already exists, don't duplicate it // If the reference already exists, only duplicate for roots
if _, ok = db.nodes[parent].children[child]; ok { if _, ok = db.nodes[parent].children[child]; ok && parent != (common.Hash{}) {
return return
} }
node.parents++ node.parents++
db.nodes[parent].children[child] = struct{}{} db.nodes[parent].children[child]++
} }
// Dereference removes an existing reference from a parent node to a child node. // Dereference removes an existing reference from a parent node to a child node.
@ -211,9 +211,13 @@ func (db *Database) Dereference(child common.Hash, parent common.Hash) {
// dereference is the private locked version of Dereference. // dereference is the private locked version of Dereference.
func (db *Database) dereference(child common.Hash, parent common.Hash) { func (db *Database) dereference(child common.Hash, parent common.Hash) {
// Dereference the parent-child relationship // Dereference the parent-child
delete(db.nodes[parent].children, child) node := db.nodes[parent]
node.children[child]--
if node.children[child] == 0 {
delete(node.children, child)
}
// If the node does not exist, it's a previously comitted node. // If the node does not exist, it's a previously comitted node.
node, ok := db.nodes[child] node, ok := db.nodes[child]
if !ok { if !ok {