Skip to content

Commit

Permalink
Add encode method to Node interface
Browse files Browse the repository at this point in the history
  • Loading branch information
trinhdn97 committed Jul 16, 2023
1 parent c7be6cc commit 340bf2b
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 51 deletions.
7 changes: 6 additions & 1 deletion trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ type rawNode []byte
func (n rawNode) Cache() (HashNode, bool) { panic("this should never end up in a live trie") }
func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") }

func (n rawNode) EncodeRLP(w io.Writer) error {
_, err := w.Write(n)
return err
}

// rawFullNode represents only the useful data content of a full Node, with the
// caches and flags stripped out to minimize its data storage. This type honors
// the same RLP encoding as the original parent.
Expand Down Expand Up @@ -184,7 +189,7 @@ func (n *cachedNode) obj(hash common.Hash) Node {

// forChilds invokes the callback for all the tracked children of this Node,
// both the implicit ones from inside the Node as well as the explicit ones
//from outside the Node.
// from outside the Node.
func (n *cachedNode) forChilds(onChild func(hash common.Hash)) {
for child := range n.children {
onChild(child)
Expand Down
82 changes: 42 additions & 40 deletions trie/hasher.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2019 The go-ethereum Authors
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
Expand All @@ -23,31 +23,22 @@ import (
"github.com/tomochain/tomochain/rlp"
)

type sliceBuffer []byte

func (b *sliceBuffer) Write(data []byte) (n int, err error) {
*b = append(*b, data...)
return len(data), nil
}

func (b *sliceBuffer) Reset() {
*b = (*b)[:0]
}

// hasher is a type used for the trie Hash operation. A hasher has some
// internal preallocated temp space
type hasher struct {
sha crypto.KeccakState
tmp sliceBuffer
parallel bool // Whether to use paralallel threads when hashing
tmp []byte
encbuf rlp.EncoderBuffer
parallel bool // Whether to use parallel threads when hashing
}

// hasherPool holds pureHashers
var hasherPool = sync.Pool{
New: func() interface{} {
return &hasher{
tmp: make(sliceBuffer, 0, 550), // cap is as large as a full FullNode.
sha: crypto.NewKeccakState(),
tmp: make([]byte, 0, 550), // cap is as large as a full fullNode.
sha: crypto.NewKeccakState(),
encbuf: rlp.NewEncoderBuffer(nil),
}
},
}
Expand All @@ -62,14 +53,14 @@ func returnHasherToPool(h *hasher) {
hasherPool.Put(h)
}

// hash collapses a Node down into a hash Node, also returning a copy of the
// original Node initialized with the computed hash to replace the original one.
// hash collapses a node down into a hash node, also returning a copy of the
// original node initialized with the computed hash to replace the original one.
func (h *hasher) hash(n Node, force bool) (hashed Node, cached Node) {
// We're not storing the Node, just hashing, use available cached data
// Return the cached hash if it's available
if hash, _ := n.Cache(); hash != nil {
return hash, n
}
// Trie not processed yet or needs storage, walk the children
// Trie not processed yet, walk the children
switch n := n.(type) {
case *ShortNode:
collapsed, cached := h.hashShortNodeChildren(n)
Expand Down Expand Up @@ -97,11 +88,11 @@ func (h *hasher) hash(n Node, force bool) (hashed Node, cached Node) {
}
}

// hashShortNodeChildren collapses the short Node. The returned collapsed Node
// hashShortNodeChildren collapses the short node. The returned collapsed node
// holds a live reference to the Key, and must not be modified.
// The cached
func (h *hasher) hashShortNodeChildren(n *ShortNode) (collapsed, cached *ShortNode) {
// Hash the short Node's child, caching the newly hashed subtree
// Hash the short node's child, caching the newly hashed subtree
collapsed, cached = n.copy(), n.copy()
// Previously, we did copy this one. We don't seem to need to actually
// do that, since we don't overwrite/reuse keys
Expand All @@ -116,7 +107,7 @@ func (h *hasher) hashShortNodeChildren(n *ShortNode) (collapsed, cached *ShortNo
}

func (h *hasher) hashFullNodeChildren(n *FullNode) (collapsed *FullNode, cached *FullNode) {
// Hash the full Node's children, caching the newly hashed subtrees
// Hash the full node's children, caching the newly hashed subtrees
cached = n.copy()
collapsed = n.copy()
if h.parallel {
Expand Down Expand Up @@ -147,35 +138,46 @@ func (h *hasher) hashFullNodeChildren(n *FullNode) (collapsed *FullNode, cached
return collapsed, cached
}

// shortnodeToHash creates a HashNode from a ShortNode. The supplied shortnode
// shortnodeToHash creates a hashNode from a shortNode. The supplied shortnode
// should have hex-type Key, which will be converted (without modification)
// 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 {
h.tmp.Reset()
if err := rlp.Encode(&h.tmp, n); err != nil {
panic("encode error: " + err.Error())
}
n.encode(h.encbuf)
enc := h.encodedBytes()

if len(h.tmp) < 32 && !force {
if len(enc) < 32 && !force {
return n // Nodes smaller than 32 bytes are stored inside their parent
}
return h.hashData(h.tmp)
return h.hashData(enc)
}

// shortnodeToHash is used to creates a HashNode from a set of hashNodes, (which
// shortnodeToHash is used to creates a hashNode from a set of hashNodes, (which
// may contain nil values)
func (h *hasher) fullnodeToHash(n *FullNode, force bool) Node {
h.tmp.Reset()
// Generate the RLP encoding of the Node
if err := n.EncodeRLP(&h.tmp); err != nil {
panic("encode error: " + err.Error())
}
n.encode(h.encbuf)
enc := h.encodedBytes()

if len(h.tmp) < 32 && !force {
if len(enc) < 32 && !force {
return n // Nodes smaller than 32 bytes are stored inside their parent
}
return h.hashData(h.tmp)
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
}

// hashData hashes the provided data
Expand All @@ -188,8 +190,8 @@ func (h *hasher) hashData(data []byte) HashNode {
}

// proofHash is used to construct trie proofs, and returns the 'collapsed'
// Node (for later RLP encoding) aswell as the hashed Node -- unless the
// Node is smaller than 32 bytes, in which case it will be returned as is.
// node (for later RLP encoding) as well as the hashed node -- unless the
// node is smaller than 32 bytes, in which case it will be returned as is.
// This method does not do anything on value- or hash-nodes.
func (h *hasher) proofHash(original Node) (collapsed, hashed Node) {
switch n := original.(type) {
Expand Down
14 changes: 4 additions & 10 deletions trie/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b
type Node interface {
fstring(string) string
Cache() (HashNode, bool)
encode(w rlp.EncoderBuffer)
}

type (
Expand All @@ -52,16 +53,9 @@ var nilValueNode = ValueNode(nil)

// EncodeRLP encodes a full Node into the consensus RLP format.
func (n *FullNode) EncodeRLP(w io.Writer) error {
var nodes [17]Node

for i, child := range &n.Children {
if child != nil {
nodes[i] = child
} else {
nodes[i] = nilValueNode
}
}
return rlp.Encode(w, nodes)
eb := rlp.NewEncoderBuffer(w)
n.encode(eb)
return eb.Flush()
}

func (n *FullNode) copy() *FullNode { copy := *n; return &copy }
Expand Down
64 changes: 64 additions & 0 deletions trie/node_enc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package trie

import (
"github.com/tomochain/tomochain/rlp"
)

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()
for _, c := range n.Children {
if c != nil {
c.encode(w)
} else {
w.Write(rlp.EmptyString)
}
}
w.ListEnd(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)
}
w.ListEnd(offset)
}

func (n HashNode) encode(w rlp.EncoderBuffer) {
w.WriteBytes(n)
}

func (n ValueNode) encode(w rlp.EncoderBuffer) {
w.WriteBytes(n)
}

func (n rawNode) encode(w rlp.EncoderBuffer) {
w.Write(n)
}

0 comments on commit 340bf2b

Please sign in to comment.