Skip to content

Commit

Permalink
core, core/state, trie: enterprise hand-tuned multi-level caching
Browse files Browse the repository at this point in the history
  • Loading branch information
karalabe committed May 26, 2016
1 parent a7434fd commit 748d1c1
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 133 deletions.
7 changes: 6 additions & 1 deletion core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
tstart = time.Now()

nonceChecked = make([]bool, len(chain))
statedb *state.StateDB
)

// Start the parallel nonce verifier.
Expand Down Expand Up @@ -885,7 +886,11 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {

// Create a new statedb using the parent block and report an
// error if it fails.
statedb, err := state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
if statedb == nil {
statedb, err = state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
} else {
err = statedb.Reset(chain[i-1].Root())
}
if err != nil {
reportBlock(block, err)
return i, err
Expand Down
22 changes: 22 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
}, nil
}

// Reset clears out all emphemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations.
func (self *StateDB) Reset(root common.Hash) error {
var (
err error
tr = self.trie
)
if self.trie.Hash() != root {
if tr, err = trie.NewSecure(root, self.db); err != nil {
return err
}
}
*self = StateDB{
db: self.db,
trie: tr,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}
return nil
}

func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
self.thash = thash
self.bhash = bhash
Expand Down
24 changes: 13 additions & 11 deletions trie/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byt
switch node := node.(type) {
case fullNode:
if len(key) > 0 {
k := self.next(node[key[0]], key[1:], isIterStart)
k := self.next(node.Children[key[0]], key[1:], isIterStart)
if k != nil {
return append([]byte{key[0]}, k...)
}
Expand All @@ -74,7 +74,7 @@ func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byt
}

for i := r; i < 16; i++ {
k := self.key(node[i])
k := self.key(node.Children[i])
if k != nil {
return append([]byte{i}, k...)
}
Expand Down Expand Up @@ -130,12 +130,12 @@ func (self *Iterator) key(node interface{}) []byte {
}
return append(k, self.key(node.Val)...)
case fullNode:
if node[16] != nil {
self.Value = node[16].(valueNode)
if node.Children[16] != nil {
self.Value = node.Children[16].(valueNode)
return []byte{16}
}
for i := 0; i < 16; i++ {
k := self.key(node[i])
k := self.key(node.Children[i])
if k != nil {
return append([]byte{byte(i)}, k...)
}
Expand Down Expand Up @@ -175,7 +175,7 @@ type NodeIterator struct {

// NewNodeIterator creates an post-order trie iterator.
func NewNodeIterator(trie *Trie) *NodeIterator {
if bytes.Compare(trie.Root(), emptyRoot.Bytes()) == 0 {
if trie.Hash() == emptyState {
return new(NodeIterator)
}
return &NodeIterator{trie: trie}
Expand Down Expand Up @@ -205,9 +205,11 @@ func (it *NodeIterator) step() error {
}
// Initialize the iterator if we've just started, or pop off the old node otherwise
if len(it.stack) == 0 {
it.stack = append(it.stack, &nodeIteratorState{node: it.trie.root, child: -1})
// Always start with a collapsed root
root := it.trie.Hash()
it.stack = append(it.stack, &nodeIteratorState{node: hashNode(root[:]), child: -1})
if it.stack[0].node == nil {
return fmt.Errorf("root node missing: %x", it.trie.Root())
return fmt.Errorf("root node missing: %x", it.trie.Hash())
}
} else {
it.stack = it.stack[:len(it.stack)-1]
Expand All @@ -225,11 +227,11 @@ func (it *NodeIterator) step() error {
}
if node, ok := parent.node.(fullNode); ok {
// Full node, traverse all children, then the node itself
if parent.child >= len(node) {
if parent.child >= len(node.Children) {
break
}
for parent.child++; parent.child < len(node); parent.child++ {
if current := node[parent.child]; current != nil {
for parent.child++; parent.child < len(node.Children); parent.child++ {
if current := node.Children[parent.child]; current != nil {
it.stack = append(it.stack, &nodeIteratorState{node: current, parent: ancestor, child: -1})
break
}
Expand Down
60 changes: 39 additions & 21 deletions trie/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,36 @@ var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b

type node interface {
fstring(string) string
cache() (hashNode, bool)
}

type (
fullNode [17]node
fullNode struct {
Children [17]node // Actual trie node data to encode/decode (needs custom encoder)
hash hashNode // Cached hash of the node to prevent rehashing (may be nil)
dirty bool // Cached flag whether the node's new or already stored
}
shortNode struct {
Key []byte
Val node
Key []byte
Val node
hash hashNode // Cached hash of the node to prevent rehashing (may be nil)
dirty bool // Cached flag whether the node's new or already stored
}
hashNode []byte
valueNode []byte
)

// EncodeRLP encodes a full node into the consensus RLP format.
func (n fullNode) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, n.Children)
}

// Cache accessors to retrieve precalculated values (avoid lengthy type switches).
func (n fullNode) cache() (hashNode, bool) { return n.hash, n.dirty }
func (n shortNode) cache() (hashNode, bool) { return n.hash, n.dirty }
func (n hashNode) cache() (hashNode, bool) { return nil, true }
func (n valueNode) cache() (hashNode, bool) { return nil, true }

// Pretty printing.
func (n fullNode) String() string { return n.fstring("") }
func (n shortNode) String() string { return n.fstring("") }
Expand All @@ -49,7 +67,7 @@ func (n valueNode) String() string { return n.fstring("") }

func (n fullNode) fstring(ind string) string {
resp := fmt.Sprintf("[\n%s ", ind)
for i, node := range n {
for i, node := range n.Children {
if node == nil {
resp += fmt.Sprintf("%s: <nil> ", indices[i])
} else {
Expand All @@ -68,16 +86,16 @@ func (n valueNode) fstring(ind string) string {
return fmt.Sprintf("%x ", []byte(n))
}

func mustDecodeNode(dbkey, buf []byte) node {
n, err := decodeNode(buf)
func mustDecodeNode(hash, buf []byte) node {
n, err := decodeNode(hash, buf)
if err != nil {
panic(fmt.Sprintf("node %x: %v", dbkey, err))
panic(fmt.Sprintf("node %x: %v", hash, err))
}
return n
}

// decodeNode parses the RLP encoding of a trie node.
func decodeNode(buf []byte) (node, error) {
func decodeNode(hash, buf []byte) (node, error) {
if len(buf) == 0 {
return nil, io.ErrUnexpectedEOF
}
Expand All @@ -87,18 +105,18 @@ func decodeNode(buf []byte) (node, error) {
}
switch c, _ := rlp.CountValues(elems); c {
case 2:
n, err := decodeShort(elems)
n, err := decodeShort(hash, buf, elems)
return n, wrapError(err, "short")
case 17:
n, err := decodeFull(elems)
n, err := decodeFull(hash, buf, elems)
return n, wrapError(err, "full")
default:
return nil, fmt.Errorf("invalid number of list elements: %v", c)
}
}

func decodeShort(buf []byte) (node, error) {
kbuf, rest, err := rlp.SplitString(buf)
func decodeShort(hash, buf, elems []byte) (node, error) {
kbuf, rest, err := rlp.SplitString(elems)
if err != nil {
return nil, err
}
Expand All @@ -109,30 +127,30 @@ func decodeShort(buf []byte) (node, error) {
if err != nil {
return nil, fmt.Errorf("invalid value node: %v", err)
}
return shortNode{key, valueNode(val)}, nil
return shortNode{key, valueNode(val), hash, false}, nil
}
r, _, err := decodeRef(rest)
if err != nil {
return nil, wrapError(err, "val")
}
return shortNode{key, r}, nil
return shortNode{key, r, hash, false}, nil
}

func decodeFull(buf []byte) (fullNode, error) {
var n fullNode
func decodeFull(hash, buf, elems []byte) (fullNode, error) {
n := fullNode{hash: hash}
for i := 0; i < 16; i++ {
cld, rest, err := decodeRef(buf)
cld, rest, err := decodeRef(elems)
if err != nil {
return n, wrapError(err, fmt.Sprintf("[%d]", i))
}
n[i], buf = cld, rest
n.Children[i], elems = cld, rest
}
val, _, err := rlp.SplitString(buf)
val, _, err := rlp.SplitString(elems)
if err != nil {
return n, err
}
if len(val) > 0 {
n[16] = valueNode(val)
n.Children[16] = valueNode(val)
}
return n, nil
}
Expand All @@ -152,7 +170,7 @@ func decodeRef(buf []byte) (node, []byte, error) {
err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen)
return nil, buf, err
}
n, err := decodeNode(buf)
n, err := decodeNode(nil, buf)
return n, rest, err
case kind == rlp.String && len(val) == 0:
// empty node
Expand Down
8 changes: 4 additions & 4 deletions trie/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
}
nodes = append(nodes, n)
case fullNode:
tn = n[key[0]]
tn = n.Children[key[0]]
key = key[1:]
nodes = append(nodes, n)
case hashNode:
Expand All @@ -77,7 +77,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
for i, n := range nodes {
// Don't bother checking for errors here since hasher panics
// if encoding doesn't work and we're not writing to any database.
n, _ = t.hasher.replaceChildren(n, nil)
n, _, _ = t.hasher.hashChildren(n, nil)
hn, _ := t.hasher.store(n, nil, false)
if _, ok := hn.(hashNode); ok || i == 0 {
// If the node's database encoding is a hash (or is the
Expand All @@ -103,7 +103,7 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value
if !bytes.Equal(sha.Sum(nil), wantHash) {
return nil, fmt.Errorf("bad proof node %d: hash mismatch", i)
}
n, err := decodeNode(buf)
n, err := decodeNode(wantHash, buf)
if err != nil {
return nil, fmt.Errorf("bad proof node %d: %v", i, err)
}
Expand Down Expand Up @@ -139,7 +139,7 @@ func get(tn node, key []byte) ([]byte, node) {
tn = n.Val
key = key[len(n.Key):]
case fullNode:
tn = n[key[0]]
tn = n.Children[key[0]]
key = key[1:]
case hashNode:
return key, n
Expand Down
4 changes: 2 additions & 2 deletions trie/secure_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) {
}
t.secKeyCache = make(map[string][]byte)
}
n, err := t.hashRoot(db)
n, clean, err := t.hashRoot(db)
if err != nil {
return (common.Hash{}), err
}
t.root = n
t.root = clean
return common.BytesToHash(n.(hashNode)), nil
}

Expand Down
13 changes: 7 additions & 6 deletions trie/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, c
if root == emptyRoot {
return
}
blob, _ := s.database.Get(root.Bytes())
if local, err := decodeNode(blob); local != nil && err == nil {
key := root.Bytes()
blob, _ := s.database.Get(key)
if local, err := decodeNode(key, blob); local != nil && err == nil {
return
}
// Assemble the new sub-trie sync request
Expand Down Expand Up @@ -152,7 +153,7 @@ func (s *TrieSync) Process(results []SyncResult) (int, error) {
continue
}
// Decode the node data content and update the request
node, err := decodeNode(item.Data)
node, err := decodeNode(item.Hash[:], item.Data)
if err != nil {
return i, err
}
Expand Down Expand Up @@ -213,9 +214,9 @@ func (s *TrieSync) children(req *request) ([]*request, error) {
}}
case fullNode:
for i := 0; i < 17; i++ {
if node[i] != nil {
if node.Children[i] != nil {
children = append(children, child{
node: &node[i],
node: &node.Children[i],
depth: req.depth + 1,
})
}
Expand All @@ -238,7 +239,7 @@ func (s *TrieSync) children(req *request) ([]*request, error) {
if node, ok := (*child.node).(hashNode); ok {
// Try to resolve the node from the local database
blob, _ := s.database.Get(node)
if local, err := decodeNode(blob); local != nil && err == nil {
if local, err := decodeNode(node[:], blob); local != nil && err == nil {
*child.node = local
continue
}
Expand Down
Loading

0 comments on commit 748d1c1

Please sign in to comment.