Skip to content

Commit e3efb24

Browse files
holimangzliudan
authored andcommitted
trie: make stacktrie support binary marshal/unmarshal (ethereum#22685)
1 parent a8bd860 commit e3efb24

File tree

2 files changed

+140
-1
lines changed

2 files changed

+140
-1
lines changed

trie/stacktrie.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
package trie
1818

1919
import (
20+
"bufio"
21+
"bytes"
22+
"encoding/gob"
2023
"errors"
2124
"fmt"
25+
"io"
2226
"sync"
2327

2428
"github.com/XinFinOrg/XDPoSChain/common"
@@ -66,6 +70,96 @@ func NewStackTrie(db ethdb.KeyValueWriter) *StackTrie {
6670
}
6771
}
6872

73+
// NewFromBinary initialises a serialized stacktrie with the given db.
74+
func NewFromBinary(data []byte, db ethdb.KeyValueWriter) (*StackTrie, error) {
75+
var st StackTrie
76+
if err := st.UnmarshalBinary(data); err != nil {
77+
return nil, err
78+
}
79+
// If a database is used, we need to recursively add it to every child
80+
if db != nil {
81+
st.setDb(db)
82+
}
83+
return &st, nil
84+
}
85+
86+
// MarshalBinary implements encoding.BinaryMarshaler
87+
func (st *StackTrie) MarshalBinary() (data []byte, err error) {
88+
var (
89+
b bytes.Buffer
90+
w = bufio.NewWriter(&b)
91+
)
92+
if err := gob.NewEncoder(w).Encode(struct {
93+
Nodetype uint8
94+
Val []byte
95+
Key []byte
96+
KeyOffset uint8
97+
}{
98+
st.nodeType,
99+
st.val,
100+
st.key,
101+
uint8(st.keyOffset),
102+
}); err != nil {
103+
return nil, err
104+
}
105+
for _, child := range st.children {
106+
if child == nil {
107+
w.WriteByte(0)
108+
continue
109+
}
110+
w.WriteByte(1)
111+
if childData, err := child.MarshalBinary(); err != nil {
112+
return nil, err
113+
} else {
114+
w.Write(childData)
115+
}
116+
}
117+
w.Flush()
118+
return b.Bytes(), nil
119+
}
120+
121+
// UnmarshalBinary implements encoding.BinaryUnmarshaler
122+
func (st *StackTrie) UnmarshalBinary(data []byte) error {
123+
r := bytes.NewReader(data)
124+
return st.unmarshalBinary(r)
125+
}
126+
127+
func (st *StackTrie) unmarshalBinary(r io.Reader) error {
128+
var dec struct {
129+
Nodetype uint8
130+
Val []byte
131+
Key []byte
132+
KeyOffset uint8
133+
}
134+
gob.NewDecoder(r).Decode(&dec)
135+
st.nodeType = dec.Nodetype
136+
st.val = dec.Val
137+
st.key = dec.Key
138+
st.keyOffset = int(dec.KeyOffset)
139+
140+
var hasChild = make([]byte, 1)
141+
for i := range st.children {
142+
if _, err := r.Read(hasChild); err != nil {
143+
return err
144+
} else if hasChild[0] == 0 {
145+
continue
146+
}
147+
var child StackTrie
148+
child.unmarshalBinary(r)
149+
st.children[i] = &child
150+
}
151+
return nil
152+
}
153+
154+
func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
155+
st.db = db
156+
for _, child := range st.children {
157+
if child != nil {
158+
child.setDb(db)
159+
}
160+
}
161+
}
162+
69163
func newLeaf(ko int, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
70164
st := stackTrieFromPool(db)
71165
st.nodeType = leafNode

trie/stacktrie_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ func TestStacktrieNotModifyValues(t *testing.T) {
151151
return big.NewInt(int64(i)).Bytes()
152152
}
153153
}
154-
155154
for i := 0; i < 1000; i++ {
156155
key := common.BigToHash(keyB)
157156
value := getValue(i)
@@ -168,5 +167,51 @@ func TestStacktrieNotModifyValues(t *testing.T) {
168167
if !bytes.Equal(have, want) {
169168
t.Fatalf("item %d, have %#x want %#x", i, have, want)
170169
}
170+
171+
}
172+
}
173+
174+
// TestStacktrieSerialization tests that the stacktrie works well if we
175+
// serialize/unserialize it a lot
176+
func TestStacktrieSerialization(t *testing.T) {
177+
var (
178+
st = NewStackTrie(nil)
179+
nt, _ = New(common.Hash{}, NewDatabase(memorydb.New()))
180+
keyB = big.NewInt(1)
181+
keyDelta = big.NewInt(1)
182+
vals [][]byte
183+
keys [][]byte
184+
)
185+
getValue := func(i int) []byte {
186+
if i%2 == 0 { // large
187+
return crypto.Keccak256(big.NewInt(int64(i)).Bytes())
188+
} else { //small
189+
return big.NewInt(int64(i)).Bytes()
190+
}
191+
}
192+
for i := 0; i < 10; i++ {
193+
vals = append(vals, getValue(i))
194+
keys = append(keys, common.BigToHash(keyB).Bytes())
195+
keyB = keyB.Add(keyB, keyDelta)
196+
keyDelta.Add(keyDelta, common.Big1)
197+
}
198+
for i, k := range keys {
199+
nt.TryUpdate(k, common.CopyBytes(vals[i]))
200+
}
201+
202+
for i, k := range keys {
203+
blob, err := st.MarshalBinary()
204+
if err != nil {
205+
t.Fatal(err)
206+
}
207+
newSt, err := NewFromBinary(blob, nil)
208+
if err != nil {
209+
t.Fatal(err)
210+
}
211+
st = newSt
212+
st.TryUpdate(k, common.CopyBytes(vals[i]))
213+
}
214+
if have, want := st.Hash(), nt.Hash(); have != want {
215+
t.Fatalf("have %#x want %#x", have, want)
171216
}
172217
}

0 commit comments

Comments
 (0)