Skip to content

Commit c9f8eeb

Browse files
authored
Merge pull request #2508 from CortexFoundation/dev
reduce allocations in derivesha
2 parents d47cb89 + ddfaa76 commit c9f8eeb

File tree

9 files changed

+233
-105
lines changed

9 files changed

+233
-105
lines changed

core/types/block.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,10 @@ type storageblock struct {
237237
// block.
238238
//
239239
// relevant portions of the header.
240-
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher Hasher) *Block {
240+
//
241+
// The receipt's bloom must already calculated for the block's bloom to be
242+
// correctly calculated.
243+
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher ListHasher) *Block {
241244
if body == nil {
242245
body = &Body{}
243246
}

core/types/derive_sha.go

Lines changed: 0 additions & 59 deletions
This file was deleted.

core/types/hashing.go

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
"github.com/CortexFoundation/CortexTheseus/rlp"
2828
)
2929

30-
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
30+
// hasherPool holds LegacyKeccak256 buffer for rlpHash.
3131
var hasherPool = sync.Pool{
3232
New: func() interface{} { return crypto.NewKeccakState() },
3333
}
@@ -75,10 +75,66 @@ func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
7575
return h
7676
}
7777

78-
// TrieHasher is the tool used to calculate the hash of derivable list.
79-
// This is internal, do not use.
80-
type TrieHasher interface {
78+
// ListHasher defines the interface for computing the hash of a derivable list.
79+
type ListHasher interface {
80+
// Reset clears the internal state of the hasher, preparing it for reuse.
8181
Reset()
82-
Update([]byte, []byte) error
82+
83+
// Update inserts the given key-value pair into the hasher.
84+
// The implementation must copy the provided slices, allowing the caller
85+
// to safely modify them after the call returns.
86+
Update(key []byte, value []byte) error
87+
88+
// Hash computes and returns the final hash of all inserted key-value pairs.
8389
Hash() common.Hash
8490
}
91+
92+
// DerivableList is the input to DeriveSha.
93+
// It is implemented by the 'Transactions' and 'Receipts' types.
94+
// This is internal, do not use these methods.
95+
type DerivableList interface {
96+
Len() int
97+
EncodeIndex(int, *bytes.Buffer)
98+
}
99+
100+
// encodeForDerive encodes the element in the list at the position i into the buffer.
101+
func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte {
102+
buf.Reset()
103+
list.EncodeIndex(i, buf)
104+
return buf.Bytes()
105+
}
106+
107+
// DeriveSha creates the tree hashes of transactions, receipts, and withdrawals in a block header.
108+
func DeriveSha(list DerivableList, hasher ListHasher) common.Hash {
109+
hasher.Reset()
110+
111+
// Allocate a buffer for value encoding. As the hasher is claimed that all
112+
// supplied key value pairs will be copied by hasher and safe to reuse the
113+
// encoding buffer.
114+
valueBuf := encodeBufferPool.Get().(*bytes.Buffer)
115+
defer encodeBufferPool.Put(valueBuf)
116+
117+
// StackTrie requires values to be inserted in increasing hash order, which is not the
118+
// order that `list` provides hashes in. This insertion sequence ensures that the
119+
// order is correct.
120+
//
121+
// The error returned by hasher is omitted because hasher will produce an incorrect
122+
// hash in case any error occurs.
123+
var indexBuf []byte
124+
for i := 1; i < list.Len() && i <= 0x7f; i++ {
125+
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
126+
value := encodeForDerive(list, i, valueBuf)
127+
hasher.Update(indexBuf, value)
128+
}
129+
if list.Len() > 0 {
130+
indexBuf = rlp.AppendUint64(indexBuf[:0], 0)
131+
value := encodeForDerive(list, 0, valueBuf)
132+
hasher.Update(indexBuf, value)
133+
}
134+
for i := 0x80; i < list.Len(); i++ {
135+
indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i))
136+
value := encodeForDerive(list, i, valueBuf)
137+
hasher.Update(indexBuf, value)
138+
}
139+
return hasher.Hash()
140+
}

core/types/receipt.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ func (r Receipts) GetRlp(i int) []byte {
293293
return bytes
294294
}
295295

296+
// EncodeIndex encodes the i'th receipt to w.
297+
func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
298+
r := rs[i]
299+
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
300+
rlp.Encode(w, data)
301+
}
302+
296303
// DeriveFields fills the receipts with their computed fields based on consensus
297304
// data and contextual infos like containing block and transactions.
298305
func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, time uint64, txs []*Transaction) error {

core/types/transaction.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package types
1818

1919
import (
20+
"bytes"
2021
"container/heap"
2122
"errors"
2223
"fmt"
@@ -292,6 +293,14 @@ func (s Transactions) GetRlp(i int) []byte {
292293
return enc
293294
}
294295

296+
// EncodeIndex encodes the i'th transaction to w. Note that this does not check for errors
297+
// because we assume that *Transaction will only ever contain valid txs that were either
298+
// constructed by decoding or via public API in this package.
299+
func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) {
300+
tx := s[i]
301+
rlp.Encode(w, &tx.data)
302+
}
303+
295304
// TxDifference returns a new set of transactions that are present in a but not in b.
296305
func TxDifference(a, b Transactions) Transactions {
297306
keep := make(Transactions, 0, len(a))

trie/bytepool.go

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ func newBytesPool(sliceCap, nitems int) *bytesPool {
3232
}
3333
}
3434

35-
// Get returns a slice. Safe for concurrent use.
36-
func (bp *bytesPool) Get() []byte {
35+
// get returns a slice. Safe for concurrent use.
36+
func (bp *bytesPool) get() []byte {
3737
select {
3838
case b := <-bp.c:
3939
return b
@@ -42,18 +42,18 @@ func (bp *bytesPool) Get() []byte {
4242
}
4343
}
4444

45-
// GetWithSize returns a slice with specified byte slice size.
46-
func (bp *bytesPool) GetWithSize(s int) []byte {
47-
b := bp.Get()
45+
// getWithSize returns a slice with specified byte slice size.
46+
func (bp *bytesPool) getWithSize(s int) []byte {
47+
b := bp.get()
4848
if cap(b) < s {
4949
return make([]byte, s)
5050
}
5151
return b[:s]
5252
}
5353

54-
// Put returns a slice to the pool. Safe for concurrent use. This method
54+
// put returns a slice to the pool. Safe for concurrent use. This method
5555
// will ignore slices that are too small or too large (>3x the cap)
56-
func (bp *bytesPool) Put(b []byte) {
56+
func (bp *bytesPool) put(b []byte) {
5757
if c := cap(b); c < bp.w || c > 3*bp.w {
5858
return
5959
}
@@ -62,3 +62,40 @@ func (bp *bytesPool) Put(b []byte) {
6262
default:
6363
}
6464
}
65+
66+
// unsafeBytesPool is a pool for byte slices. It is not safe for concurrent use.
67+
type unsafeBytesPool struct {
68+
items [][]byte
69+
w int
70+
}
71+
72+
// newUnsafeBytesPool creates a new unsafeBytesPool. The sliceCap sets the
73+
// capacity of newly allocated slices, and the nitems determines how many
74+
// items the pool will hold, at maximum.
75+
func newUnsafeBytesPool(sliceCap, nitems int) *unsafeBytesPool {
76+
return &unsafeBytesPool{
77+
items: make([][]byte, 0, nitems),
78+
w: sliceCap,
79+
}
80+
}
81+
82+
// Get returns a slice with pre-allocated space.
83+
func (bp *unsafeBytesPool) get() []byte {
84+
if len(bp.items) > 0 {
85+
last := bp.items[len(bp.items)-1]
86+
bp.items = bp.items[:len(bp.items)-1]
87+
return last
88+
}
89+
return make([]byte, 0, bp.w)
90+
}
91+
92+
// put returns a slice to the pool. This method will ignore slices that are
93+
// too small or too large (>3x the cap)
94+
func (bp *unsafeBytesPool) put(b []byte) {
95+
if c := cap(b); c < bp.w || c > 3*bp.w {
96+
return
97+
}
98+
if len(bp.items) < cap(bp.items) {
99+
bp.items = append(bp.items, b)
100+
}
101+
}

trie/list_hasher.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package trie
18+
19+
import (
20+
"bytes"
21+
22+
"github.com/CortexFoundation/CortexTheseus/common"
23+
)
24+
25+
// ListHasher is a wrapper of the Merkle-Patricia-Trie, which implements
26+
// types.ListHasher. Compared to a Trie instance, the Update method of this
27+
// type always deep-copies its input slices.
28+
//
29+
// This implementation is very inefficient in terms of memory allocation,
30+
// compared with StackTrie. It exists only for correctness comparison purposes.
31+
type ListHasher struct {
32+
tr *Trie
33+
}
34+
35+
// NewListHasher initializes the list hasher.
36+
func NewListHasher() *ListHasher {
37+
return &ListHasher{
38+
tr: NewEmpty(nil),
39+
}
40+
}
41+
42+
// Reset clears the internal state prepares the ListHasher for reuse.
43+
func (h *ListHasher) Reset() {
44+
h.tr.reset()
45+
}
46+
47+
// Update inserts a key-value pair into the trie.
48+
func (h *ListHasher) Update(key []byte, value []byte) error {
49+
key, value = bytes.Clone(key), bytes.Clone(value)
50+
return h.tr.Update(key, value)
51+
}
52+
53+
// Hash computes the root hash of all inserted key-value pairs.
54+
func (h *ListHasher) Hash() common.Hash {
55+
return h.tr.Hash()
56+
}

0 commit comments

Comments
 (0)