Restacktrie with pruning (#13)

* revert snaphash to processing all accounts

* use ReStackTrie in generateTrie

* Save memory by hashing a branch if no more insert will occur
This commit is contained in:
Guillaume Ballet 2020-03-25 14:00:55 +01:00 committed by GitHub
parent 6dc45cf878
commit 62d322a1a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 18 deletions

View file

@ -18,7 +18,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
"os" "os"
@ -617,18 +616,14 @@ func snapToHash(ctx *cli.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("Could not create iterator for root %x: %v", root, err) return fmt.Errorf("Could not create iterator for root %x: %v", root, err)
} }
ollKorrekt := snapshot.CrosscheckTriehasher(it, 0, 10000) generatedRoot := snapshot.GenerateTrieRoot(it)
//generatedRoot := snapshot.GenerateTrieRoot(it) if err := it.Error(); err != nil {
//if err := it.Error(); err != nil { fmt.Printf("Iterator error: %v\n", it.Error())
// fmt.Printf("Iterator error: %v\n", it.Error())
//}
//if root != generatedRoot {
// return fmt.Errorf("Wrong hash generated, expected %x, got %x", root, generatedRoot[:])
//}
if !ollKorrekt {
return errors.New("Computer says No, @gballet\n...come on man, fix me already!")
} }
//log.Info("Generation done", "root", generatedRoot) if root != generatedRoot {
return fmt.Errorf("Wrong hash generated, expected %x, got %x", root, generatedRoot[:])
}
log.Info("Generation done", "root", generatedRoot)
return nil return nil
} }

View file

@ -36,7 +36,7 @@ type trieGeneratorFn func(in chan (leaf), out chan (common.Hash))
// GenerateTrieRoot takes an account iterator and reproduces the root hash. // GenerateTrieRoot takes an account iterator and reproduces the root hash.
func GenerateTrieRoot(it AccountIterator) common.Hash { func GenerateTrieRoot(it AccountIterator) common.Hash {
//return generateTrieRoot(it, StackGenerate) //return generateTrieRoot(it, StackGenerate)
return generateTrieRoot(it, StdGenerate) return generateTrieRoot(it, ReStackGenerate)
} }
func CrosscheckTriehasher(it AccountIterator, begin, end int) bool { func CrosscheckTriehasher(it AccountIterator, begin, end int) bool {

View file

@ -287,6 +287,7 @@ const (
extNode extNode
leafNode leafNode
emptyNode emptyNode
hashedNode
) )
func (st *ReStackTrie) TryUpdate(key, value []byte) error { func (st *ReStackTrie) TryUpdate(key, value []byte) error {
@ -318,6 +319,18 @@ func (st *ReStackTrie) insert(key, value []byte) {
st.children[idx] = NewReStackTrie() st.children[idx] = NewReStackTrie()
st.children[idx].keyOffset = st.keyOffset + 1 st.children[idx].keyOffset = st.keyOffset + 1
} }
for i := idx - 1; i >= 0; i-- {
if st.children[i] != nil {
if st.children[i].nodeType != hashedNode {
st.children[i].val = st.children[i].Hash().Bytes()
st.children[i].key = nil
st.children[i].nodeType = hashedNode
}
break
}
}
st.children[idx].insert(key, value) st.children[idx].insert(key, value)
case extNode: /* Ext */ case extNode: /* Ext */
// Compare both key chunks and see where they differ // Compare both key chunks and see where they differ
@ -415,6 +428,8 @@ func (st *ReStackTrie) insert(key, value []byte) {
// Create the two child leaves: the one containing the // Create the two child leaves: the one containing the
// original value and the one containing the new value // original value and the one containing the new value
// The child leave will be hashed directly in order to
// free up some memory.
origIdx := st.key[diffidx] origIdx := st.key[diffidx]
p.children[origIdx] = NewReStackTrie() p.children[origIdx] = NewReStackTrie()
p.children[origIdx].nodeType = leafNode p.children[origIdx].nodeType = leafNode
@ -422,6 +437,10 @@ func (st *ReStackTrie) insert(key, value []byte) {
p.children[origIdx].val = st.val p.children[origIdx].val = st.val
p.children[origIdx].keyOffset = p.keyOffset + 1 p.children[origIdx].keyOffset = p.keyOffset + 1
p.children[origIdx].val = p.children[origIdx].Hash().Bytes()
p.children[origIdx].nodeType = hashedNode
p.children[origIdx].key = nil
newIdx := key[diffidx+st.keyOffset] newIdx := key[diffidx+st.keyOffset]
p.children[newIdx] = NewReStackTrie() p.children[newIdx] = NewReStackTrie()
p.children[newIdx].nodeType = leafNode p.children[newIdx].nodeType = leafNode
@ -434,6 +453,8 @@ func (st *ReStackTrie) insert(key, value []byte) {
st.nodeType = leafNode st.nodeType = leafNode
st.key = key[st.keyOffset:] st.key = key[st.keyOffset:]
st.val = value st.val = value
case hashedNode:
panic("trying to insert into hash")
default: default:
panic("invalid type") panic("invalid type")
} }
@ -539,18 +560,24 @@ func writeHPRLP(writer io.Writer, key, val []byte, leaf bool) {
} }
func (st *ReStackTrie) Hash() (h common.Hash) { func (st *ReStackTrie) Hash() (h common.Hash) {
/* Shortcut if node is already hashed */
if st.nodeType == hashedNode {
return common.BytesToHash(st.val)
}
d := sha3.NewLegacyKeccak256() d := sha3.NewLegacyKeccak256()
switch st.nodeType { switch st.nodeType {
case 0: case branchNode:
payload := [544]byte{} payload := [544]byte{}
pos := 3 // maximum header length given what we know pos := 3 // maximum header length given what we know
for _, v := range st.children { for i, v := range st.children {
if v != nil { if v != nil {
// Write a 32 byte list to the sponge // Write a 32 byte list to the sponge
payload[pos] = 0xa0 payload[pos] = 0xa0
pos++ pos++
copy(payload[pos:pos+32], v.Hash().Bytes()) copy(payload[pos:pos+32], v.Hash().Bytes())
pos += 32 pos += 32
st.children[i] = nil // Reclaim mem from subtree
} else { } else {
// Write an empty list to the sponge // Write an empty list to the sponge
payload[pos] = 0x80 payload[pos] = 0x80
@ -579,12 +606,13 @@ func (st *ReStackTrie) Hash() (h common.Hash) {
start = 0 start = 0
} }
d.Write(payload[start:pos]) d.Write(payload[start:pos])
case 1: case extNode:
ch := st.children[0].Hash().Bytes() ch := st.children[0].Hash().Bytes()
writeHPRLP(d, st.key, ch, false) writeHPRLP(d, st.key, ch, false)
case 2: st.children[0] = nil // Reclaim mem from subtree
case leafNode:
writeHPRLP(d, st.key, st.val, true) writeHPRLP(d, st.key, st.val, true)
case 3: case emptyNode:
default: default:
panic("Invalid node type") panic("Invalid node type")
} }