diff --git a/trie/encoding.go b/trie/encoding.go index 9c862d78fa..005e8eae81 100644 --- a/trie/encoding.go +++ b/trie/encoding.go @@ -16,13 +16,20 @@ package trie +// This file implements a codec to convert between byte arrays +// and nibble (half-byte) arrays, where the latter are used for traversing the trie. +// Hence the "compact" encoded form for a key is as the byte array, which +// is (roughly) half the length of the decoded nibble array. +// Nibble arrays for keys that represent leaf nodes have a terminator flag (numerical 16) appended to the end. +// The compact encoded form uses Hex Prefix (HP) encoding, to encode the terminator status +// and whether the key length is even or odd in the first two bytes (note the original description uses only +// one byte, but that's inconvenient) + +// Encode a slice of nibbles into a HP byte array func CompactEncode(hexSlice []byte) []byte { terminator := 0 if hexSlice[len(hexSlice)-1] == 16 { terminator = 1 - } - - if terminator == 1 { hexSlice = hexSlice[:len(hexSlice)-1] } @@ -42,12 +49,16 @@ func CompactEncode(hexSlice []byte) []byte { return buf } -func CompactDecode(str []byte) []byte { - base := CompactHexDecode(str) - base = base[:len(base)-1] - if base[0] >= 2 { - base = append(base, 16) +// Decode a HP encoded byte array into a nibble array +// with terminator flag if applicable +func CompactDecode(key []byte) []byte { + base := CompactHexDecode(key) // appends the terminator flag by default + if base[0] < 2 { + // remove the terminator flag if its not in the HP + base = base[:len(base)-1] } + + // HP tells us if key length is even or odd if base[0]%2 == 1 { base = base[1:] } else { @@ -57,10 +68,13 @@ func CompactDecode(str []byte) []byte { return base } -func CompactHexDecode(str []byte) []byte { - l := len(str)*2 + 1 +// Decode a byte array into a nibble array. +// Assumes the key coressponds to a terminator node (ie appends 16) +// CompactHexDecode is called immediately by the Get/Update/Remove functions. +func CompactHexDecode(key []byte) []byte { + l := len(key)*2 + 1 var nibbles = make([]byte, l) - for i, b := range str { + for i, b := range key { nibbles[i*2] = b / 16 nibbles[i*2+1] = b % 16 } @@ -68,12 +82,13 @@ func CompactHexDecode(str []byte) []byte { return nibbles } -func DecodeCompact(key []byte) []byte { - l := len(key) / 2 +// This is really a compact encoding of nibbles +// without hex-prefix +func DecodeCompact(nibbles []byte) []byte { + l := len(nibbles) / 2 var res = make([]byte, l) for i := 0; i < l; i++ { - v1, v0 := key[2*i], key[2*i+1] - res[i] = v1*16 + v0 + res[i] = 16*nibbles[2*i] + nibbles[2*i+1] } return res } diff --git a/trie/fullnode.go b/trie/fullnode.go index 8ff019ec43..de09fd17f3 100644 --- a/trie/fullnode.go +++ b/trie/fullnode.go @@ -16,6 +16,9 @@ package trie +// FullNode represents the main node type for a radix tree. +// The first 16 children are branches for each possible next letter in the key. +// The final slot is for a terminating node (a ValueNode), whose key ends at the FullNode. type FullNode struct { trie *Trie nodes [17]Node diff --git a/trie/hashnode.go b/trie/hashnode.go index d4a0bc7ec9..675bf32040 100644 --- a/trie/hashnode.go +++ b/trie/hashnode.go @@ -18,6 +18,10 @@ package trie import "github.com/ethereum/go-ethereum/common" +// HashNode represents a node that is too big to be a ShortNode. +// The actual node of interest (probably a FullNode) is rlp encoded and hashed, +// yielding the key of the HashNode. The key can be used to fetch the actual +// node from the database type HashNode struct { key []byte trie *Trie diff --git a/trie/node.go b/trie/node.go index 9d49029ded..bda39b5bd5 100644 --- a/trie/node.go +++ b/trie/node.go @@ -25,7 +25,7 @@ type Node interface { Copy(*Trie) Node // All nodes, for now, return them self Dirty() bool fstring(string) string - Hash() interface{} + Hash() interface{} // only really a hash if size(node) > 32 RlpData() interface{} setDirty(dirty bool) } diff --git a/trie/shortnode.go b/trie/shortnode.go index 569d5f1096..c27d35ba68 100644 --- a/trie/shortnode.go +++ b/trie/shortnode.go @@ -18,9 +18,12 @@ package trie import "github.com/ethereum/go-ethereum/common" +// ShortNode holds the nibble key for a ValueNode, HashNode, or FullNode. +// Note a ShortNode should never hold another ShortNode, as they can just +// be combined into one ShortNode type ShortNode struct { trie *Trie - key []byte + key []byte // hex-prefixed bytes value Node dirty bool } @@ -30,7 +33,6 @@ func NewShortNode(t *Trie, key []byte, value Node) *ShortNode { } func (self *ShortNode) Value() Node { self.value = self.trie.trans(self.value) - return self.value } func (self *ShortNode) Dirty() bool { return self.dirty } @@ -48,6 +50,7 @@ func (self *ShortNode) Hash() interface{} { return self.trie.store(self) } +// returns nibbles func (self *ShortNode) Key() []byte { return CompactDecode(self.key) } diff --git a/trie/trie.go b/trie/trie.go index abf48a8509..e08fb3bf6e 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -358,6 +358,7 @@ func (self *Trie) mknode(value *common.Value) Node { return NewValueNode(self, value.Bytes()) } +// resolve HashNodes by fetching from the db func (self *Trie) trans(node Node) Node { switch node := node.(type) { case *HashNode: diff --git a/trie/valuenode.go b/trie/valuenode.go index 0afa64d54c..85cfc8a4af 100644 --- a/trie/valuenode.go +++ b/trie/valuenode.go @@ -18,6 +18,8 @@ package trie import "github.com/ethereum/go-ethereum/common" +// ValueNode represents a leaf node, a terminal point in the tree. +// It is implied that if you found the ValueNode, then you know its key type ValueNode struct { trie *Trie data []byte