diff --git a/go.mod b/go.mod index b4d077fc47..6124aa9fdc 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 + github.com/qianbin/drlp v0.0.0-20231230065804-db474f66bf91 github.com/rs/cors v1.7.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 diff --git a/go.sum b/go.sum index bab51b1345..8e6134c3ab 100644 --- a/go.sum +++ b/go.sum @@ -522,6 +522,8 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 h1:cZC+usqsYgHtlBaGulVnZ1hfKAi8iWtujBnRLQE698c= github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= +github.com/qianbin/drlp v0.0.0-20231230065804-db474f66bf91 h1:N1pcRULtPB7CndtexGDr94ai8wsJLx55AKYfo7F6qEs= +github.com/qianbin/drlp v0.0.0-20231230065804-db474f66bf91/go.mod h1:OnClEjurpFUtR3RUCauP9HxNNl8xjfGAOv0kWYTznOc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/trie/committer.go b/trie/committer.go index 92163cdb3b..11540938a8 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -139,7 +139,7 @@ func (c *committer) store(path []byte, n node) node { } // Collect the dirty node to nodeset for return. nhash := common.BytesToHash(hash) - c.nodes.AddNode(path, trienode.New(nhash, nodeToBytes(n))) + c.nodes.AddNode(path, trienode.New(nhash, n.encode(nil))) // Collect the corresponding leaf node if it's required. We don't check // full node since it's impossible to store value in fullNode. The key diff --git a/trie/hasher.go b/trie/hasher.go index 1e063d8020..7fc58c09e0 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -20,7 +20,6 @@ import ( "sync" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" ) @@ -29,7 +28,6 @@ import ( type hasher struct { sha crypto.KeccakState tmp []byte - encbuf rlp.EncoderBuffer parallel bool // Whether to use parallel threads when hashing } @@ -37,9 +35,8 @@ type hasher struct { var hasherPool = sync.Pool{ New: func() interface{} { return &hasher{ - tmp: make([]byte, 0, 550), // cap is as large as a full fullNode. - sha: sha3.NewLegacyKeccak256().(crypto.KeccakState), - encbuf: rlp.NewEncoderBuffer(nil), + tmp: make([]byte, 0, 550), // cap is as large as a full fullNode. + sha: sha3.NewLegacyKeccak256().(crypto.KeccakState), } }, } @@ -143,41 +140,23 @@ func (h *hasher) hashFullNodeChildren(n *fullNode) (collapsed *fullNode, cached // into compact form for RLP encoding. // If the rlp data is smaller than 32 bytes, `nil` is returned. func (h *hasher) shortnodeToHash(n *shortNode, force bool) node { - n.encode(h.encbuf) - enc := h.encodedBytes() + h.tmp = n.encode(h.tmp[:0]) - if len(enc) < 32 && !force { + if len(h.tmp) < 32 && !force { return n // Nodes smaller than 32 bytes are stored inside their parent } - return h.hashData(enc) + return h.hashData(h.tmp) } // fullnodeToHash is used to create a hashNode from a fullNode, (which // may contain nil values) func (h *hasher) fullnodeToHash(n *fullNode, force bool) node { - n.encode(h.encbuf) - enc := h.encodedBytes() + h.tmp = n.encode(h.tmp[:0]) - if len(enc) < 32 && !force { + if len(h.tmp) < 32 && !force { return n // Nodes smaller than 32 bytes are stored inside their parent } - return h.hashData(enc) -} - -// encodedBytes returns the result of the last encoding operation on h.encbuf. -// This also resets the encoder buffer. -// -// All node encoding must be done like this: -// -// node.encode(h.encbuf) -// enc := h.encodedBytes() -// -// This convention exists because node.encode can only be inlined/escape-analyzed when -// called on a concrete receiver type. -func (h *hasher) encodedBytes() []byte { - h.tmp = h.encbuf.AppendToBytes(h.tmp[:0]) - h.encbuf.Reset(nil) - return h.tmp + return h.hashData(h.tmp) } // hashData hashes the provided data diff --git a/trie/iterator.go b/trie/iterator.go index 83ccc0740f..53597b8e55 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -242,7 +242,7 @@ func (it *nodeIterator) LeafProof() [][]byte { // Gather nodes that end up as hash nodes (or the root) node, hashed := hasher.proofHash(item.node) if _, ok := hashed.(hashNode); ok || i == 0 { - proofs = append(proofs, nodeToBytes(node)) + proofs = append(proofs, node.encode(nil)) } } return proofs diff --git a/trie/node.go b/trie/node.go index 15bbf62f1c..a8b2b4014c 100644 --- a/trie/node.go +++ b/trie/node.go @@ -29,7 +29,8 @@ var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b type node interface { cache() (hashNode, bool) - encode(w rlp.EncoderBuffer) + // encode appends encoded bytes to buf and returns then extended buffer. + encode(buf []byte) []byte fstring(string) string } @@ -53,9 +54,11 @@ var nilValueNode = valueNode(nil) // EncodeRLP encodes a full node into the consensus RLP format. func (n *fullNode) EncodeRLP(w io.Writer) error { - eb := rlp.NewEncoderBuffer(w) - n.encode(eb) - return eb.Flush() + h := newHasher(false) + defer returnHasherToPool(h) + h.tmp = n.encode(h.tmp[:0]) + _, err := w.Write(h.tmp) + return err } func (n *fullNode) copy() *fullNode { copy := *n; return © } diff --git a/trie/node_enc.go b/trie/node_enc.go index 1b2eca682f..1309a0063f 100644 --- a/trie/node_enc.go +++ b/trie/node_enc.go @@ -17,48 +17,46 @@ package trie import ( - "github.com/ethereum/go-ethereum/rlp" + "github.com/qianbin/drlp" ) -func nodeToBytes(n node) []byte { - w := rlp.NewEncoderBuffer(nil) - n.encode(w) - result := w.ToBytes() - w.Flush() - return result -} - -func (n *fullNode) encode(w rlp.EncoderBuffer) { - offset := w.List() +func (n *fullNode) encode(buf []byte) []byte { + if buf == nil { + buf = make([]byte, 0, 550) + } + offset := len(buf) for _, c := range n.Children { if c != nil { - c.encode(w) + buf = c.encode(buf) } else { - w.Write(rlp.EmptyString) + buf = drlp.AppendUint(buf, 0) } } - w.ListEnd(offset) + return drlp.EndList(buf, offset) } -func (n *shortNode) encode(w rlp.EncoderBuffer) { - offset := w.List() - w.WriteBytes(n.Key) - if n.Val != nil { - n.Val.encode(w) - } else { - w.Write(rlp.EmptyString) +func (n *shortNode) encode(buf []byte) []byte { + if buf == nil { + buf = make([]byte, 0, len(n.Key)+40) } - w.ListEnd(offset) + offset := len(buf) + buf = drlp.AppendString(buf, n.Key) + if n.Val != nil { + buf = n.Val.encode(buf) + } else { + buf = drlp.AppendUint(buf, 0) + } + return drlp.EndList(buf, offset) } -func (n hashNode) encode(w rlp.EncoderBuffer) { - w.WriteBytes(n) +func (n hashNode) encode(buf []byte) []byte { + return drlp.AppendString(buf, n) } -func (n valueNode) encode(w rlp.EncoderBuffer) { - w.WriteBytes(n) +func (n valueNode) encode(buf []byte) []byte { + return drlp.AppendString(buf, n) } -func (n rawNode) encode(w rlp.EncoderBuffer) { - w.Write(n) +func (n rawNode) encode(buf []byte) []byte { + return append(buf, n...) } diff --git a/trie/node_test.go b/trie/node_test.go index 9b8b33748f..8503e563c6 100644 --- a/trie/node_test.go +++ b/trie/node_test.go @@ -108,7 +108,7 @@ func BenchmarkEncodeShortNode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - nodeToBytes(node) + node.encode(nil) } } @@ -126,7 +126,7 @@ func BenchmarkEncodeFullNode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - nodeToBytes(node) + node.encode(nil) } } @@ -140,7 +140,7 @@ func BenchmarkDecodeShortNode(b *testing.B) { Key: []byte{0x1, 0x2}, Val: hashNode(randBytes(32)), } - blob := nodeToBytes(node) + blob := node.encode(nil) hash := crypto.Keccak256(blob) b.ResetTimer() @@ -161,7 +161,7 @@ func BenchmarkDecodeShortNodeUnsafe(b *testing.B) { Key: []byte{0x1, 0x2}, Val: hashNode(randBytes(32)), } - blob := nodeToBytes(node) + blob := node.encode(nil) hash := crypto.Keccak256(blob) b.ResetTimer() @@ -182,7 +182,7 @@ func BenchmarkDecodeFullNode(b *testing.B) { for i := 0; i < 16; i++ { node.Children[i] = hashNode(randBytes(32)) } - blob := nodeToBytes(node) + blob := node.encode(nil) hash := crypto.Keccak256(blob) b.ResetTimer() @@ -203,7 +203,7 @@ func BenchmarkDecodeFullNodeUnsafe(b *testing.B) { for i := 0; i < 16; i++ { node.Children[i] = hashNode(randBytes(32)) } - blob := nodeToBytes(node) + blob := node.encode(nil) hash := crypto.Keccak256(blob) b.ResetTimer() diff --git a/trie/proof.go b/trie/proof.go index a526a53402..406487385e 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -90,7 +90,7 @@ func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { if hash, ok := hn.(hashNode); ok || i == 0 { // If the node's database encoding is a hash (or is the // root node), it becomes a proof element. - enc := nodeToBytes(n) + enc := n.encode(nil) if !ok { hash = hasher.hashData(enc) } diff --git a/trie/stacktrie.go b/trie/stacktrie.go index f2f5355c49..8708e8b88b 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -377,8 +377,8 @@ func (t *StackTrie) hash(st *stNode, path []byte) { st.children[i] = nil stPool.Put(child.reset()) // Release child back to pool. } - nodes.encode(t.h.encbuf) - blob = t.h.encodedBytes() + t.h.tmp = nodes.encode(t.h.tmp[:0]) + blob = t.h.tmp case extNode: // recursively hash and commit child as the first step @@ -400,8 +400,8 @@ func (t *StackTrie) hash(st *stNode, path []byte) { } else { n.Val = hashNode(st.children[0].val) } - n.encode(t.h.encbuf) - blob = t.h.encodedBytes() + t.h.tmp = n.encode(t.h.tmp[:0]) + blob = t.h.tmp stPool.Put(st.children[0].reset()) // Release child back to pool. st.children[0] = nil @@ -410,8 +410,8 @@ func (t *StackTrie) hash(st *stNode, path []byte) { st.key = append(st.key, byte(16)) n := shortNode{Key: hexToCompactInPlace(st.key), Val: valueNode(st.val)} - n.encode(t.h.encbuf) - blob = t.h.encodedBytes() + t.h.tmp = n.encode(t.h.tmp[:0]) + blob = t.h.tmp default: panic("invalid node type")