forked from cerc-io/plugeth
trie: make stacktrie support binary marshal/unmarshal (#22685)
This commit is contained in:
parent
d7bfb978ba
commit
581539c6ee
@ -17,8 +17,12 @@
|
|||||||
package trie
|
package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -66,6 +70,96 @@ func NewStackTrie(db ethdb.KeyValueWriter) *StackTrie {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFromBinary initialises a serialized stacktrie with the given db.
|
||||||
|
func NewFromBinary(data []byte, db ethdb.KeyValueWriter) (*StackTrie, error) {
|
||||||
|
var st StackTrie
|
||||||
|
if err := st.UnmarshalBinary(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// If a database is used, we need to recursively add it to every child
|
||||||
|
if db != nil {
|
||||||
|
st.setDb(db)
|
||||||
|
}
|
||||||
|
return &st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (st *StackTrie) MarshalBinary() (data []byte, err error) {
|
||||||
|
var (
|
||||||
|
b bytes.Buffer
|
||||||
|
w = bufio.NewWriter(&b)
|
||||||
|
)
|
||||||
|
if err := gob.NewEncoder(w).Encode(struct {
|
||||||
|
Nodetype uint8
|
||||||
|
Val []byte
|
||||||
|
Key []byte
|
||||||
|
KeyOffset uint8
|
||||||
|
}{
|
||||||
|
st.nodeType,
|
||||||
|
st.val,
|
||||||
|
st.key,
|
||||||
|
uint8(st.keyOffset),
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, child := range st.children {
|
||||||
|
if child == nil {
|
||||||
|
w.WriteByte(0)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.WriteByte(1)
|
||||||
|
if childData, err := child.MarshalBinary(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
w.Write(childData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (st *StackTrie) UnmarshalBinary(data []byte) error {
|
||||||
|
r := bytes.NewReader(data)
|
||||||
|
return st.unmarshalBinary(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *StackTrie) unmarshalBinary(r io.Reader) error {
|
||||||
|
var dec struct {
|
||||||
|
Nodetype uint8
|
||||||
|
Val []byte
|
||||||
|
Key []byte
|
||||||
|
KeyOffset uint8
|
||||||
|
}
|
||||||
|
gob.NewDecoder(r).Decode(&dec)
|
||||||
|
st.nodeType = dec.Nodetype
|
||||||
|
st.val = dec.Val
|
||||||
|
st.key = dec.Key
|
||||||
|
st.keyOffset = int(dec.KeyOffset)
|
||||||
|
|
||||||
|
var hasChild = make([]byte, 1)
|
||||||
|
for i := range st.children {
|
||||||
|
if _, err := r.Read(hasChild); err != nil {
|
||||||
|
return err
|
||||||
|
} else if hasChild[0] == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var child StackTrie
|
||||||
|
child.unmarshalBinary(r)
|
||||||
|
st.children[i] = &child
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
|
||||||
|
st.db = db
|
||||||
|
for _, child := range st.children {
|
||||||
|
if child != nil {
|
||||||
|
child.setDb(db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newLeaf(ko int, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
|
func newLeaf(ko int, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
|
||||||
st := stackTrieFromPool(db)
|
st := stackTrieFromPool(db)
|
||||||
st.nodeType = leafNode
|
st.nodeType = leafNode
|
||||||
|
@ -151,7 +151,6 @@ func TestStacktrieNotModifyValues(t *testing.T) {
|
|||||||
return big.NewInt(int64(i)).Bytes()
|
return big.NewInt(int64(i)).Bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
key := common.BigToHash(keyB)
|
key := common.BigToHash(keyB)
|
||||||
value := getValue(i)
|
value := getValue(i)
|
||||||
@ -168,5 +167,51 @@ func TestStacktrieNotModifyValues(t *testing.T) {
|
|||||||
if !bytes.Equal(have, want) {
|
if !bytes.Equal(have, want) {
|
||||||
t.Fatalf("item %d, have %#x want %#x", i, have, want)
|
t.Fatalf("item %d, have %#x want %#x", i, have, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestStacktrieSerialization tests that the stacktrie works well if we
|
||||||
|
// serialize/unserialize it a lot
|
||||||
|
func TestStacktrieSerialization(t *testing.T) {
|
||||||
|
var (
|
||||||
|
st = NewStackTrie(nil)
|
||||||
|
nt, _ = New(common.Hash{}, NewDatabase(memorydb.New()))
|
||||||
|
keyB = big.NewInt(1)
|
||||||
|
keyDelta = big.NewInt(1)
|
||||||
|
vals [][]byte
|
||||||
|
keys [][]byte
|
||||||
|
)
|
||||||
|
getValue := func(i int) []byte {
|
||||||
|
if i%2 == 0 { // large
|
||||||
|
return crypto.Keccak256(big.NewInt(int64(i)).Bytes())
|
||||||
|
} else { //small
|
||||||
|
return big.NewInt(int64(i)).Bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
vals = append(vals, getValue(i))
|
||||||
|
keys = append(keys, common.BigToHash(keyB).Bytes())
|
||||||
|
keyB = keyB.Add(keyB, keyDelta)
|
||||||
|
keyDelta.Add(keyDelta, common.Big1)
|
||||||
|
}
|
||||||
|
for i, k := range keys {
|
||||||
|
nt.TryUpdate(k, common.CopyBytes(vals[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, k := range keys {
|
||||||
|
blob, err := st.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
newSt, err := NewFromBinary(blob, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
st = newSt
|
||||||
|
st.TryUpdate(k, common.CopyBytes(vals[i]))
|
||||||
|
}
|
||||||
|
if have, want := st.Hash(), nt.Hash(); have != want {
|
||||||
|
t.Fatalf("have %#x want %#x", have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user