Skip to content

Commit 6c8310e

Browse files
gballetholiman
andauthored
trie: use stacktrie for Derivesha operation (ethereum#21407)
core/types: use stacktrie for derivesha trie: add stacktrie file trie: fix linter core/types: use stacktrie for derivesha rebased: adapt stacktrie to the newer version of DeriveSha Co-authored-by: Martin Holst Swende <[email protected]> More linter fixes review feedback: no key offset for nodes converted to hashes trie: use EncodeRLP for full nodes core/types: insert txs in order in derivesha trie: tests for derivesha with stacktrie trie: make stacktrie use pooled hashers trie: make stacktrie reuse tmp slice space trie: minor polishes on stacktrie trie/stacktrie: less rlp dancing core/types: explain the contorsions in DeriveSha ci: fix goimport errors trie: clear mem on subtrie hashing squashme: linter fix stracktrie: use pooling, less allocs (ethereum#3) trie: in-place hex prefix, reduce allocs and add rawNode.EncodeRLP Reintroduce the `[]node` method, add the missing `EncodeRLP` implementation for `rawNode` and calculate the hex prefix in place. Co-authored-by: Martin Holst Swende <[email protected]> Co-authored-by: Martin Holst Swende <[email protected]>
1 parent 4ee11b0 commit 6c8310e

File tree

9 files changed

+738
-8
lines changed

9 files changed

+738
-8
lines changed

core/block_validator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
6262
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
6363
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
6464
}
65-
if hash := types.DeriveSha(block.Transactions(), new(trie.Trie)); hash != header.TxHash {
65+
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
6666
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
6767
}
6868
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
@@ -90,7 +90,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
9090
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
9191
}
9292
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
93-
receiptSha := types.DeriveSha(receipts, new(trie.Trie))
93+
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
9494
if receiptSha != header.ReceiptHash {
9595
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
9696
}

core/types/derive_sha.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/ethereum/go-ethereum/rlp"
2424
)
2525

26-
// DerivableList is the interface which can derive the hash.
2726
type DerivableList interface {
2827
Len() int
2928
GetRlp(i int) []byte
@@ -39,7 +38,22 @@ type Hasher interface {
3938
func DeriveSha(list DerivableList, hasher Hasher) common.Hash {
4039
hasher.Reset()
4140
keybuf := new(bytes.Buffer)
42-
for i := 0; i < list.Len(); i++ {
41+
42+
// StackTrie requires values to be inserted in increasing
43+
// hash order, which is not the order that `list` provides
44+
// hashes in. This insertion sequence ensures that the
45+
// order is correct.
46+
for i := 1; i < list.Len() && i <= 0x7f; i++ {
47+
keybuf.Reset()
48+
rlp.Encode(keybuf, uint(i))
49+
hasher.Update(keybuf.Bytes(), list.GetRlp(i))
50+
}
51+
if list.Len() > 0 {
52+
keybuf.Reset()
53+
rlp.Encode(keybuf, uint(0))
54+
hasher.Update(keybuf.Bytes(), list.GetRlp(0))
55+
}
56+
for i := 0x80; i < list.Len(); i++ {
4357
keybuf.Reset()
4458
rlp.Encode(keybuf, uint(i))
4559
hasher.Update(keybuf.Bytes(), list.GetRlp(i))

eth/downloader/queue.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi
774774
q.lock.Lock()
775775
defer q.lock.Unlock()
776776
validate := func(index int, header *types.Header) error {
777-
if types.DeriveSha(types.Transactions(txLists[index]), new(trie.Trie)) != header.TxHash {
777+
if types.DeriveSha(types.Transactions(txLists[index]), trie.NewStackTrie(nil)) != header.TxHash {
778778
return errInvalidBody
779779
}
780780
if types.CalcUncleHash(uncleLists[index]) != header.UncleHash {
@@ -799,7 +799,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int,
799799
q.lock.Lock()
800800
defer q.lock.Unlock()
801801
validate := func(index int, header *types.Header) error {
802-
if types.DeriveSha(types.Receipts(receiptList[index]), new(trie.Trie)) != header.ReceiptHash {
802+
if types.DeriveSha(types.Receipts(receiptList[index]), trie.NewStackTrie(nil)) != header.ReceiptHash {
803803
return errInvalidReceipt
804804
}
805805
return nil

eth/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
715715
log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash())
716716
break // TODO(karalabe): return error eventually, but wait a few releases
717717
}
718-
if hash := types.DeriveSha(request.Block.Transactions(), new(trie.Trie)); hash != request.Block.TxHash() {
718+
if hash := types.DeriveSha(request.Block.Transactions(), trie.NewStackTrie(nil)); hash != request.Block.TxHash() {
719719
log.Warn("Propagated block has invalid body", "have", hash, "exp", request.Block.TxHash())
720720
break // TODO(karalabe): return error eventually, but wait a few releases
721721
}

trie/database.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ type rawNode []byte
9999
func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
100100
func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") }
101101

102+
func (n rawNode) EncodeRLP(w io.Writer) error {
103+
_, err := w.Write([]byte(n))
104+
return err
105+
}
106+
102107
// rawFullNode represents only the useful data content of a full node, with the
103108
// caches and flags stripped out to minimize its data storage. This type honors
104109
// the same RLP encoding as the original parent.
@@ -199,7 +204,7 @@ func forGatherChildren(n node, onChild func(hash common.Hash)) {
199204
}
200205
case hashNode:
201206
onChild(common.BytesToHash(n))
202-
case valueNode, nil:
207+
case valueNode, nil, rawNode:
203208
default:
204209
panic(fmt.Sprintf("unknown node type: %T", n))
205210
}

trie/encoding.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,35 @@ func hexToCompact(hex []byte) []byte {
5151
return buf
5252
}
5353

54+
// hexToCompactInPlace places the compact key in input buffer, returning the length
55+
// needed for the representation
56+
func hexToCompactInPlace(hex []byte) int {
57+
var (
58+
hexLen = len(hex) // length of the hex input
59+
firstByte = byte(0)
60+
)
61+
// Check if we have a terminator there
62+
if hexLen > 0 && hex[hexLen-1] == 16 {
63+
firstByte = 1 << 5
64+
hexLen-- // last part was the terminator, ignore that
65+
}
66+
var (
67+
binLen = hexLen/2 + 1
68+
ni = 0 // index in hex
69+
bi = 1 // index in bin (compact)
70+
)
71+
if hexLen&1 == 1 {
72+
firstByte |= 1 << 4 // odd flag
73+
firstByte |= hex[0] // first nibble is contained in the first byte
74+
ni++
75+
}
76+
for ; ni < hexLen; bi, ni = bi+1, ni+2 {
77+
hex[bi] = hex[ni]<<4 | hex[ni+1]
78+
}
79+
hex[0] = firstByte
80+
return binLen
81+
}
82+
5483
func compactToHex(compact []byte) []byte {
5584
if len(compact) == 0 {
5685
return compact

trie/encoding_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package trie
1818

1919
import (
2020
"bytes"
21+
"encoding/hex"
22+
"math/rand"
2123
"testing"
2224
)
2325

@@ -75,6 +77,40 @@ func TestHexKeybytes(t *testing.T) {
7577
}
7678
}
7779

80+
func TestHexToCompactInPlace(t *testing.T) {
81+
for i, keyS := range []string{
82+
"00",
83+
"060a040c0f000a090b040803010801010900080d090a0a0d0903000b10",
84+
"10",
85+
} {
86+
hexBytes, _ := hex.DecodeString(keyS)
87+
exp := hexToCompact(hexBytes)
88+
sz := hexToCompactInPlace(hexBytes)
89+
got := hexBytes[:sz]
90+
if !bytes.Equal(exp, got) {
91+
t.Fatalf("test %d: encoding err\ninp %v\ngot %x\nexp %x\n", i, keyS, got, exp)
92+
}
93+
}
94+
}
95+
96+
func TestHexToCompactInPlaceRandom(t *testing.T) {
97+
for i := 0; i < 10000; i++ {
98+
l := rand.Intn(128)
99+
key := make([]byte, l)
100+
rand.Read(key)
101+
hexBytes := keybytesToHex(key)
102+
hexOrig := []byte(string(hexBytes))
103+
exp := hexToCompact(hexBytes)
104+
sz := hexToCompactInPlace(hexBytes)
105+
got := hexBytes[:sz]
106+
107+
if !bytes.Equal(exp, got) {
108+
t.Fatalf("encoding err \ncpt %x\nhex %x\ngot %x\nexp %x\n",
109+
key, hexOrig, got, exp)
110+
}
111+
}
112+
}
113+
78114
func BenchmarkHexToCompact(b *testing.B) {
79115
testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
80116
for i := 0; i < b.N; i++ {

0 commit comments

Comments
 (0)