trie: ensure shallow copies of Trie can be used concurrently (WIP)

This commit is contained in:
Felix Lange 2016-09-25 20:49:02 +02:00
parent 863d166c7b
commit e7394723e2
4 changed files with 40 additions and 36 deletions

View file

@ -76,7 +76,7 @@ func (it *NodeIterator) step() error {
} }
// Initialize the iterator if we've just started // Initialize the iterator if we've just started
if it.stateIt == nil { if it.stateIt == nil {
it.stateIt = trie.NewNodeIterator(it.state.trie.Trie) it.stateIt = it.state.trie.NodeIterator()
} }
// If we had data nodes previously, we surely have at least state nodes // If we had data nodes previously, we surely have at least state nodes
if it.dataIt != nil { if it.dataIt != nil {

View file

@ -70,15 +70,13 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }
} }
if t.hasher == nil { hasher := newHasher()
t.hasher = newHasher()
}
proof := make([]rlp.RawValue, 0, len(nodes)) proof := make([]rlp.RawValue, 0, len(nodes))
for i, n := range nodes { for i, n := range nodes {
// Don't bother checking for errors here since hasher panics // Don't bother checking for errors here since hasher panics
// if encoding doesn't work and we're not writing to any database. // if encoding doesn't work and we're not writing to any database.
n, _, _ = t.hasher.hashChildren(n, nil) n, _, _ = hasher.hashChildren(n, nil)
hn, _ := t.hasher.store(n, nil, false) hn, _ := hasher.store(n, nil, false)
if _, ok := hn.(hashNode); ok || i == 0 { if _, ok := hn.(hashNode); ok || i == 0 {
// If the node's database encoding is a hash (or is the // If the node's database encoding is a hash (or is the
// root node), it becomes a proof element. // root node), it becomes a proof element.

View file

@ -17,8 +17,6 @@
package trie package trie
import ( import (
"hash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -38,11 +36,9 @@ var secureKeyPrefix = []byte("secure-key-")
// //
// SecureTrie is not safe for concurrent use. // SecureTrie is not safe for concurrent use.
type SecureTrie struct { type SecureTrie struct {
*Trie trie Trie
hash hash.Hash
hashKeyBuf []byte hashKeyBuf []byte
secKeyBuf []byte secKeyBuf [64]byte
secKeyCache map[string][]byte secKeyCache map[string][]byte
} }
@ -61,7 +57,7 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) {
return nil, err return nil, err
} }
return &SecureTrie{ return &SecureTrie{
Trie: trie, trie: *trie,
secKeyCache: make(map[string][]byte), secKeyCache: make(map[string][]byte),
}, nil }, nil
} }
@ -80,7 +76,7 @@ func (t *SecureTrie) Get(key []byte) []byte {
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
// If a node was not found in the database, a MissingNodeError is returned. // If a node was not found in the database, a MissingNodeError is returned.
func (t *SecureTrie) TryGet(key []byte) ([]byte, error) { func (t *SecureTrie) TryGet(key []byte) ([]byte, error) {
return t.Trie.TryGet(t.hashKey(key)) return t.trie.TryGet(t.hashKey(key))
} }
// Update associates key with value in the trie. Subsequent calls to // Update associates key with value in the trie. Subsequent calls to
@ -105,7 +101,7 @@ func (t *SecureTrie) Update(key, value []byte) {
// If a node was not found in the database, a MissingNodeError is returned. // If a node was not found in the database, a MissingNodeError is returned.
func (t *SecureTrie) TryUpdate(key, value []byte) error { func (t *SecureTrie) TryUpdate(key, value []byte) error {
hk := t.hashKey(key) hk := t.hashKey(key)
err := t.Trie.TryUpdate(hk, value) err := t.trie.TryUpdate(hk, value)
if err != nil { if err != nil {
return err return err
} }
@ -125,7 +121,7 @@ func (t *SecureTrie) Delete(key []byte) {
func (t *SecureTrie) TryDelete(key []byte) error { func (t *SecureTrie) TryDelete(key []byte) error {
hk := t.hashKey(key) hk := t.hashKey(key)
delete(t.secKeyCache, string(hk)) delete(t.secKeyCache, string(hk))
return t.Trie.TryDelete(hk) return t.trie.TryDelete(hk)
} }
// GetKey returns the sha3 preimage of a hashed key that was // GetKey returns the sha3 preimage of a hashed key that was
@ -134,7 +130,7 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
if key, ok := t.secKeyCache[string(shaKey)]; ok { if key, ok := t.secKeyCache[string(shaKey)]; ok {
return key return key
} }
key, _ := t.Trie.db.Get(t.secKey(shaKey)) key, _ := t.trie.db.Get(t.secKey(shaKey))
return key return key
} }
@ -144,7 +140,23 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
// Committing flushes nodes from memory. Subsequent Get calls will load nodes // Committing flushes nodes from memory. Subsequent Get calls will load nodes
// from the database. // from the database.
func (t *SecureTrie) Commit() (root common.Hash, err error) { func (t *SecureTrie) Commit() (root common.Hash, err error) {
return t.CommitTo(t.db) return t.CommitTo(t.trie.db)
}
func (t *SecureTrie) Hash() common.Hash {
return t.trie.Hash()
}
func (t *SecureTrie) Root() []byte {
return t.trie.Root()
}
func (t *SecureTrie) Iterator() *Iterator {
return t.trie.Iterator()
}
func (t *SecureTrie) NodeIterator() *NodeIterator {
return NewNodeIterator(&t.trie)
} }
// CommitTo writes all nodes and the secure hash pre-images to the given database. // CommitTo writes all nodes and the secure hash pre-images to the given database.
@ -162,27 +174,24 @@ func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) {
} }
t.secKeyCache = make(map[string][]byte) t.secKeyCache = make(map[string][]byte)
} }
n, clean, err := t.hashRoot(db) n, clean, err := t.trie.hashRoot(db)
if err != nil { if err != nil {
return (common.Hash{}), err return (common.Hash{}), err
} }
t.root = clean t.trie.root = clean
return common.BytesToHash(n.(hashNode)), nil return common.BytesToHash(n.(hashNode)), nil
} }
func (t *SecureTrie) secKey(key []byte) []byte { func (t *SecureTrie) secKey(key []byte) []byte {
t.secKeyBuf = append(t.secKeyBuf[:0], secureKeyPrefix...) buf := append(t.secKeyBuf[:0], secureKeyPrefix...)
t.secKeyBuf = append(t.secKeyBuf, key...) buf = append(buf, key...)
return t.secKeyBuf return buf
} }
func (t *SecureTrie) hashKey(key []byte) []byte { func (t *SecureTrie) hashKey(key []byte) []byte {
if t.hash == nil { hash := sha3.NewKeccak256()
t.hash = sha3.NewKeccak256() hash.Reset()
t.hashKeyBuf = make([]byte, 32) hash.Write(key)
} buf := hash.Sum(t.hashKeyBuf[:0])
t.hash.Reset() return buf
t.hash.Write(key)
t.hashKeyBuf = t.hash.Sum(t.hashKeyBuf[:0])
return t.hashKeyBuf
} }

View file

@ -72,7 +72,6 @@ type Trie struct {
root node root node
db Database db Database
originalRoot common.Hash originalRoot common.Hash
*hasher
} }
// New creates a trie with an existing root node from db. // New creates a trie with an existing root node from db.
@ -474,10 +473,8 @@ func (t *Trie) hashRoot(db DatabaseWriter) (node, node, error) {
if t.root == nil { if t.root == nil {
return hashNode(emptyRoot.Bytes()), nil, nil return hashNode(emptyRoot.Bytes()), nil, nil
} }
if t.hasher == nil { hasher := newHasher()
t.hasher = newHasher() return hasher.hash(t.root, db, true)
}
return t.hasher.hash(t.root, db, true)
} }
type hasher struct { type hasher struct {