mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-29 07:07:16 +00:00
900c03b1ba
* Debug bin commitment * Fix print * Fix print * Print * Print * Print * Print * Print * Print * Print * Print * Fix binString * Fix binString * Fix binString * Print * Print * Print * Print * Print * Print * Print * Print * Print * Print * Print * Print * Print * No print * No print * Clean up * Fix lint * Fixing test * Skip some tests Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local> Co-authored-by: Alex Sharp <alexsharp@Alexs-MacBook-Pro.local>
915 lines
22 KiB
Go
915 lines
22 KiB
Go
package commitment
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ledgerwatch/erigon-lib/common/length"
|
|
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
type BinPatriciaTrie struct {
|
|
root *RootNode
|
|
trace bool
|
|
stat stat
|
|
keccak keccakState
|
|
}
|
|
|
|
type stat struct {
|
|
hashesTotal uint64
|
|
nodesTotal uint64
|
|
}
|
|
|
|
func NewBinaryPatriciaTrie() *BinPatriciaTrie {
|
|
return &BinPatriciaTrie{
|
|
keccak: sha3.NewLegacyKeccak256().(keccakState),
|
|
}
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) Update(key, value []byte) {
|
|
keyPath := newBitstring(key)
|
|
if t.root == nil {
|
|
t.root = &RootNode{
|
|
Node: &Node{
|
|
Key: key,
|
|
Value: value,
|
|
},
|
|
CommonPrefix: keyPath,
|
|
}
|
|
t.stat.nodesTotal++
|
|
return
|
|
}
|
|
|
|
edge, keyPathRest, splitAt, latest := t.root.Walk(keyPath, func(_ *Node) {})
|
|
if len(edge) == 0 && len(keyPathRest) == 0 {
|
|
latest.Value = value
|
|
return
|
|
}
|
|
pathToLatest := keyPath[:len(keyPath)-len(keyPathRest)]
|
|
|
|
t.stat.nodesTotal++
|
|
newLeaf := &Node{P: latest, Key: key, Value: value}
|
|
latest.splitEdge(pathToLatest, edge[:splitAt], edge[splitAt:], keyPathRest, newLeaf, t.hash)
|
|
if latest.P == nil {
|
|
t.root.CommonPrefix = edge[:splitAt]
|
|
}
|
|
}
|
|
|
|
// Key describes path in trie to value. When UpdateHashed is used,
|
|
// hashed key describes path to the leaf node and plainKey is stored in the leaf node Key field.
|
|
func (t *BinPatriciaTrie) UpdateHashed(plainKey, hashedKey, value []byte, isStorage bool) (updates map[string][]byte) {
|
|
keyPath := newBitstring(hashedKey)
|
|
updates = make(map[string][]byte)
|
|
if t.root == nil {
|
|
t.root = &RootNode{
|
|
Node: &Node{
|
|
Key: plainKey,
|
|
Value: value,
|
|
isStorage: isStorage,
|
|
},
|
|
CommonPrefix: keyPath,
|
|
}
|
|
|
|
t.stat.nodesTotal++
|
|
t.hash(t.root.Node, keyPath, 0)
|
|
|
|
touchMap := uint16(1 << keyPath[len(keyPath)-1])
|
|
|
|
updates[keyPath.String()] = encodeNodeUpdate(t.root.Node, touchMap, touchMap, nil)
|
|
return updates
|
|
}
|
|
|
|
touchedNodes := make([]*Node, 0)
|
|
|
|
edge, keyPathRest, splitAt, latest := t.root.Walk(keyPath, func(fn *Node) { fn.hash = fn.hash[:0]; touchedNodes = append(touchedNodes, fn) })
|
|
pathToLatest := keyPath[:len(keyPath)-len(keyPathRest)]
|
|
|
|
var touchMap uint16
|
|
if len(edge) == 0 && len(keyPathRest) == 0 { // we found the leaf
|
|
latest.Value = value
|
|
t.hash(latest, bitstring{}, 0)
|
|
|
|
touchMap = 1 << edge[len(edge)-1]
|
|
updates[pathToLatest.String()] = encodeNodeUpdate(latest, touchMap, touchMap, nil)
|
|
return updates
|
|
}
|
|
|
|
// split existing edge and insert new leaf
|
|
t.stat.nodesTotal++
|
|
|
|
newLeaf := &Node{P: latest, Key: plainKey, Value: value, isStorage: isStorage}
|
|
updates = latest.splitEdge(pathToLatest, edge[:splitAt], edge[splitAt:], keyPathRest, newLeaf, t.hash)
|
|
if latest.P == nil {
|
|
t.root.CommonPrefix = edge[:splitAt]
|
|
}
|
|
return updates
|
|
}
|
|
|
|
// Get returns value stored by provided key.
|
|
func (t *BinPatriciaTrie) Get(key []byte) ([]byte, bool) {
|
|
keyPath := newBitstring(key)
|
|
if t.root == nil {
|
|
return nil, false
|
|
}
|
|
|
|
edge, keyPathRest, _, latest := t.root.Walk(keyPath, func(_ *Node) {})
|
|
if len(edge) == 0 && len(keyPathRest) == 0 {
|
|
if latest.Value == nil {
|
|
switch {
|
|
case len(latest.LPrefix) == 0 && latest.L != nil:
|
|
return latest.L.Value, true
|
|
case len(latest.RPrefix) == 0 && latest.R != nil:
|
|
return latest.R.Value, true
|
|
}
|
|
}
|
|
return latest.Value, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// Get returns value stored by provided key.
|
|
func (t *BinPatriciaTrie) getNode(key []byte) *Node {
|
|
keyPath := newBitstring(key)
|
|
if t.root == nil {
|
|
return nil
|
|
}
|
|
|
|
edge, keyPathRest, _, latest := t.root.Walk(keyPath, func(_ *Node) {})
|
|
if len(edge) == 0 && len(keyPathRest) == 0 {
|
|
return latest
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) RootHash() ([]byte, error) {
|
|
if t.root == nil {
|
|
return EmptyRootHash, nil
|
|
}
|
|
return t.hash(t.root.Node, t.root.CommonPrefix, 0), nil
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) ProcessUpdates(plainKeys, hashedKeys [][]byte, updates []Update) (branchNodeUpdates map[string][]byte, err error) {
|
|
branchNodeUpdates = make(map[string][]byte)
|
|
for i, update := range updates {
|
|
account := new(Account)
|
|
node := t.getNode(hashedKeys[i]) // check if key exist
|
|
if node != nil && !node.isStorage {
|
|
account.decode(node.Value)
|
|
}
|
|
|
|
// apply supported updates
|
|
if update.Flags == DELETE_UPDATE {
|
|
//continue
|
|
if node != nil {
|
|
if node.P != nil {
|
|
meltPrefix := node.P.deleteChild(node)
|
|
if node.P.P == nil {
|
|
t.root.CommonPrefix = append(t.root.CommonPrefix, meltPrefix...)
|
|
}
|
|
} else { // remove root
|
|
t.root = nil
|
|
}
|
|
t.stat.nodesTotal--
|
|
branchNodeUpdates[hexToBin(hashedKeys[i]).String()] = []byte{}
|
|
}
|
|
continue
|
|
}
|
|
if update.Flags&BALANCE_UPDATE != 0 {
|
|
account.Balance.Set(&update.Balance)
|
|
}
|
|
if update.Flags&NONCE_UPDATE != 0 {
|
|
account.Nonce = update.Nonce
|
|
}
|
|
if update.Flags&CODE_UPDATE != 0 {
|
|
if account.CodeHash == nil {
|
|
account.CodeHash = make([]byte, len(update.CodeHashOrStorage))
|
|
}
|
|
copy(account.CodeHash, update.CodeHashOrStorage[:])
|
|
}
|
|
|
|
aux := make([]byte, 0)
|
|
isStorage := false
|
|
if update.Flags&STORAGE_UPDATE != 0 {
|
|
isStorage = true
|
|
aux = update.CodeHashOrStorage[:update.ValLength]
|
|
}
|
|
|
|
// aux is not empty only when storage update is there
|
|
if len(aux) == 0 {
|
|
aux = account.encode(aux)
|
|
}
|
|
|
|
ukey := t.UpdateHashed(plainKeys[i], hashedKeys[i], aux, isStorage)
|
|
for pref, val := range ukey {
|
|
branchNodeUpdates[pref] = val
|
|
if val != nil && t.trace {
|
|
fmt.Printf("%q => %s\n", pref, branchToString2(val))
|
|
}
|
|
}
|
|
for pref, upd := range t.rootHashWithUpdates() {
|
|
v, ex := branchNodeUpdates[pref]
|
|
if ex {
|
|
upd = append(v[:4], upd[4:]...)
|
|
}
|
|
branchNodeUpdates[pref] = upd
|
|
}
|
|
}
|
|
|
|
return branchNodeUpdates, nil
|
|
}
|
|
|
|
func DecodeNodeFromUpdate(buf []byte) (touch, after uint16, node Node, err error) {
|
|
if len(buf) < 5 {
|
|
return
|
|
}
|
|
|
|
touch = binary.BigEndian.Uint16(buf[:2])
|
|
after = binary.BigEndian.Uint16(buf[2:4])
|
|
bits, pos := PartFlags(buf[4]), 5
|
|
|
|
if bits&ACCOUNT_PLAIN_PART != 0 {
|
|
n, aux, err := decodeSizedBuffer(buf[pos:])
|
|
if err != nil {
|
|
return touch, after, Node{}, fmt.Errorf("decode account plain key: %w", err)
|
|
}
|
|
pos += n
|
|
node.Key = aux
|
|
}
|
|
|
|
if bits&STORAGE_PLAIN_PART != 0 {
|
|
n, aux, err := decodeSizedBuffer(buf[pos:])
|
|
if err != nil {
|
|
return touch, after, Node{}, fmt.Errorf("decode storage plain key: %w", err)
|
|
}
|
|
pos += n
|
|
node.Key = aux
|
|
node.isStorage = true
|
|
}
|
|
|
|
if bits&HASH_PART != 0 {
|
|
n, aux, err := decodeSizedBuffer(buf[pos:])
|
|
if err != nil {
|
|
return touch, after, Node{}, fmt.Errorf("decode node hash: %w", err)
|
|
}
|
|
pos += n
|
|
_ = pos
|
|
node.hash = aux
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func encodeNodeUpdate(node *Node, touched, after uint16, branchData []byte) []byte {
|
|
var numBuf [binary.MaxVarintLen64]byte
|
|
binary.BigEndian.PutUint16(numBuf[0:], touched)
|
|
binary.BigEndian.PutUint16(numBuf[2:], after)
|
|
|
|
if branchData == nil {
|
|
branchData = make([]byte, 4, 32)
|
|
}
|
|
copy(branchData[:4], numBuf[:])
|
|
|
|
var fieldBits PartFlags
|
|
if node.Value != nil {
|
|
fieldBits = ACCOUNT_PLAIN_PART
|
|
if node.isStorage {
|
|
fieldBits = STORAGE_PLAIN_PART
|
|
}
|
|
}
|
|
if len(node.hash) == length.Hash {
|
|
fieldBits |= HASH_PART
|
|
}
|
|
|
|
branchData = append(branchData, byte(fieldBits))
|
|
if fieldBits&(ACCOUNT_PLAIN_PART|STORAGE_PLAIN_PART) != 0 {
|
|
n := binary.PutUvarint(numBuf[:], uint64(len(node.Key)))
|
|
branchData = append(branchData, append(numBuf[:n], node.Key...)...)
|
|
}
|
|
|
|
if fieldBits&HASH_PART > 0 {
|
|
n := binary.PutUvarint(numBuf[:], uint64(len(node.hash)))
|
|
branchData = append(branchData, append(numBuf[:n], node.hash...)...)
|
|
}
|
|
|
|
return branchData
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) Reset() {
|
|
t.root = nil
|
|
fmt.Printf("trie %v\n", t.StatString())
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) ResetFns(
|
|
branchFn func(prefix []byte) ([]byte, error),
|
|
accountFn func(plainKey []byte, cell *Cell) error,
|
|
storageFn func(plainKey []byte, cell *Cell) error,
|
|
) {
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) Variant() TrieVariant { return VariantBinPatriciaTrie }
|
|
|
|
func (t *BinPatriciaTrie) SetTrace(b bool) { t.trace = b }
|
|
|
|
type RootNode struct {
|
|
*Node
|
|
CommonPrefix bitstring
|
|
}
|
|
|
|
// There are three types of nodes:
|
|
// - Leaf (with a value and without branches)
|
|
// - Branch (with left and right child)
|
|
// - Root - Either leaf or branch. When root is branch, it's Key contains their common prefix as a bitstring.
|
|
type Node struct {
|
|
L, R, P *Node // left and right child, parent. For root P is nil
|
|
LPrefix bitstring // left child prefix, always begins with 0
|
|
RPrefix bitstring // right child prefix, always begins with 1
|
|
hash []byte // node hash
|
|
Key []byte // same as common prefix, useful for debugging, actual key should be reconstructed by path to the node
|
|
Value []byte // exists only in LEAF node
|
|
isStorage bool // if true, then Value holds storage value for the Key, otherwise it holds encoded account
|
|
}
|
|
|
|
func (n *Node) splitEdge(pathToNode, commonPath, detachedPath, restKeyPath bitstring, newLeaf *Node, hasher func(n *Node, pref bitstring, offt int) []byte) map[string][]byte {
|
|
var movedNode *Node
|
|
switch {
|
|
case n.Value == nil:
|
|
movedNode = &Node{ // move existed branch
|
|
L: n.L,
|
|
R: n.R,
|
|
P: n,
|
|
LPrefix: n.LPrefix,
|
|
RPrefix: n.RPrefix,
|
|
hash: n.hash,
|
|
}
|
|
movedNode.L.P, movedNode.R.P = movedNode, movedNode
|
|
default:
|
|
movedNode = &Node{ // move existed leaf
|
|
P: n,
|
|
Key: n.Key,
|
|
Value: n.Value,
|
|
isStorage: n.isStorage,
|
|
hash: n.hash,
|
|
}
|
|
}
|
|
newLeaf.P = n
|
|
|
|
switch restKeyPath[0] {
|
|
case 0:
|
|
n.LPrefix, n.L = restKeyPath, newLeaf
|
|
n.RPrefix, n.R = detachedPath, movedNode
|
|
case 1:
|
|
n.LPrefix, n.L = detachedPath, movedNode
|
|
n.RPrefix, n.R = restKeyPath, newLeaf
|
|
}
|
|
|
|
// node become extended, reset key and value
|
|
n.Key, n.Value, n.hash, n.isStorage = nil, nil, nil, false
|
|
hasher(n, commonPath, 0)
|
|
|
|
nodeTouch := uint16(1 << pathToNode[len(pathToNode)-1])
|
|
nodeAfter := uint16(3) // both child has been updated
|
|
updates := make(map[string][]byte, 3)
|
|
|
|
hasher(n, bitstring{}, 0)
|
|
|
|
updates[pathToNode.String()] = encodeNodeUpdate(n, nodeTouch, nodeAfter, nil)
|
|
|
|
rtouch := uint16(1 << restKeyPath[0])
|
|
updates[append(pathToNode, restKeyPath[0]).String()] = encodeNodeUpdate(newLeaf, rtouch, rtouch, nil)
|
|
|
|
if n.P == nil {
|
|
// commonPath should be set to RootNode.CommonPrefix outside the function
|
|
return updates
|
|
}
|
|
|
|
if len(commonPath) > 0 {
|
|
switch commonPath[0] {
|
|
case 1:
|
|
//if n.P != nil {
|
|
n.P.RPrefix = commonPath
|
|
//}
|
|
//n.RPrefix = commonPath
|
|
case 0:
|
|
//if n.P != nil {
|
|
n.P.LPrefix = commonPath
|
|
//}
|
|
//n.LPrefix = commonPath
|
|
}
|
|
}
|
|
return updates
|
|
}
|
|
|
|
func (n *RootNode) Walk(path bitstring, fn func(cd *Node)) (nodePath, pathRest bitstring, splitAt int, current *Node) {
|
|
nodePath = n.CommonPrefix
|
|
|
|
var bit uint8
|
|
var equal bool
|
|
for current = n.Node; current != nil; {
|
|
fn(current)
|
|
|
|
splitAt, bit, equal = nodePath.splitPoint(path)
|
|
if equal {
|
|
return bitstring{}, bitstring{}, 0, current
|
|
}
|
|
|
|
if splitAt < len(nodePath) {
|
|
return nodePath, path[splitAt:], splitAt, current
|
|
}
|
|
|
|
if splitAt == 0 || splitAt == len(nodePath) {
|
|
path = path[splitAt:]
|
|
|
|
switch bit {
|
|
case 1:
|
|
if current.R == nil {
|
|
return nodePath, path, splitAt, current
|
|
}
|
|
nodePath = current.RPrefix
|
|
current = current.R
|
|
case 0:
|
|
if current.L == nil {
|
|
return nodePath, path, splitAt, current
|
|
}
|
|
nodePath = current.LPrefix
|
|
current = current.L
|
|
}
|
|
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
return nodePath, path, splitAt, current
|
|
}
|
|
|
|
func (n *Node) deleteChild(child *Node) bitstring {
|
|
var melt *Node
|
|
var meltPrefix bitstring
|
|
|
|
// remove child data
|
|
switch child {
|
|
case n.L:
|
|
n.L, n.LPrefix = nil, nil
|
|
melt = n.R
|
|
meltPrefix = n.RPrefix
|
|
case n.R:
|
|
n.R, n.RPrefix = nil, nil
|
|
melt = n.L
|
|
meltPrefix = n.LPrefix
|
|
default:
|
|
panic("could delete only child nodes")
|
|
}
|
|
melt.P = n.P
|
|
|
|
// merge parent path to skip this half-branch node
|
|
if n.P != nil {
|
|
switch {
|
|
case n.P.L == n:
|
|
n.P.L, n.P.LPrefix = melt, append(n.P.LPrefix, meltPrefix...)
|
|
case n.P.R == n:
|
|
n.P.R, n.P.RPrefix = melt, append(n.P.RPrefix, meltPrefix...)
|
|
default:
|
|
panic("failed to merge parent path")
|
|
}
|
|
} else { // n is root
|
|
n.LPrefix, n.RPrefix = melt.LPrefix, melt.RPrefix
|
|
n.Key = melt.Key
|
|
n.Value = melt.Value
|
|
n.L, n.R, n.Value = melt.L, melt.R, melt.Value
|
|
n.hash = n.hash[:0]
|
|
}
|
|
return meltPrefix
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) StatString() string {
|
|
s := t.stat
|
|
return fmt.Sprintf("hashes_total %d nodes %d", s.hashesTotal, s.nodesTotal)
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) rootHashWithUpdates() map[string][]byte {
|
|
//if t.root == nil {
|
|
// return EmptyRootHash, nil
|
|
//}
|
|
//return t.hash(t.root.Node, t.root.CommonPrefix, 0), nil
|
|
updates := make(map[string][]byte)
|
|
t.hashWithUpdates(t.root.Node, t.root.CommonPrefix, &updates)
|
|
return updates
|
|
}
|
|
|
|
func (t *BinPatriciaTrie) hashWithUpdates(n *Node, pref bitstring, updates *map[string][]byte) ([]byte, []byte) {
|
|
if len(n.hash) == 32 {
|
|
return n.hash, nil
|
|
}
|
|
t.keccak.Reset()
|
|
|
|
t.stat.hashesTotal++
|
|
|
|
var hash []byte
|
|
if n.Value == nil {
|
|
// This is a branch node, so the rule is
|
|
// branch_hash = hash(left_root_hash || right_root_hash)
|
|
lkey := bitstring(make([]byte, len(pref)+len(n.LPrefix)))
|
|
copy(lkey, pref)
|
|
copy(lkey[len(pref):], n.LPrefix)
|
|
|
|
rkey := bitstring(make([]byte, len(pref)+len(n.RPrefix)))
|
|
copy(rkey, pref)
|
|
copy(rkey[len(pref):], n.RPrefix)
|
|
|
|
lh, lupd := t.hashWithUpdates(n.L, lkey, updates)
|
|
rh, rupd := t.hashWithUpdates(n.R, rkey, updates)
|
|
t.keccak.Write(lh)
|
|
t.keccak.Write(rh)
|
|
hash = t.keccak.Sum(nil)
|
|
if len(lupd) > 0 {
|
|
binary.BigEndian.PutUint16(lupd[0:], 1)
|
|
(*updates)[lkey.String()] = lupd
|
|
}
|
|
if len(rupd) > 0 {
|
|
binary.BigEndian.PutUint16(rupd[0:], 2)
|
|
(*updates)[rkey.String()] = rupd
|
|
}
|
|
|
|
if t.trace {
|
|
fmt.Printf("branch %v (%v|%v)\n", hex.EncodeToString(hash), hex.EncodeToString(lh), hex.EncodeToString(rh))
|
|
}
|
|
t.keccak.Reset()
|
|
} else {
|
|
// This is a leaf node, so the hashing rule is
|
|
// leaf_hash = hash(hash(key) || hash(leaf_value))
|
|
t.keccak.Write(n.Key)
|
|
kh := t.keccak.Sum(nil)
|
|
t.keccak.Reset()
|
|
|
|
t.keccak.Write(n.Value)
|
|
hash = t.keccak.Sum(nil)
|
|
t.keccak.Reset()
|
|
|
|
t.keccak.Write(kh)
|
|
t.keccak.Write(hash)
|
|
hash = t.keccak.Sum(nil)
|
|
t.keccak.Reset()
|
|
|
|
if t.trace {
|
|
fmt.Printf("leaf %v\n", hex.EncodeToString(hash))
|
|
}
|
|
}
|
|
|
|
n.hash = hash
|
|
upd := encodeNodeUpdate(n, 0, 3, nil)
|
|
if n.P == nil {
|
|
binary.BigEndian.PutUint16(upd[0:], 3)
|
|
(*updates)[pref.String()] = upd
|
|
}
|
|
|
|
return hash, upd
|
|
}
|
|
func (t *BinPatriciaTrie) hash(n *Node, pref bitstring, off int) []byte {
|
|
t.keccak.Reset()
|
|
t.stat.hashesTotal++
|
|
|
|
if len(n.hash) == 32 && n.P != nil {
|
|
return n.hash
|
|
}
|
|
|
|
var hash []byte
|
|
if n.Value == nil {
|
|
// This is a branch node, so the rule is
|
|
// branch_hash = hash(left_root_hash || right_root_hash)
|
|
lh := t.hash(n.L, n.LPrefix, off+len(pref))
|
|
rh := t.hash(n.R, n.RPrefix, off+len(pref))
|
|
t.keccak.Write(lh)
|
|
t.keccak.Write(rh)
|
|
hash = t.keccak.Sum(nil)
|
|
if t.trace {
|
|
fmt.Printf("branch %v (%v|%v)\n", hex.EncodeToString(hash), hex.EncodeToString(lh), hex.EncodeToString(rh))
|
|
}
|
|
t.keccak.Reset()
|
|
} else {
|
|
// This is a leaf node, so the hashing rule is
|
|
// leaf_hash = hash(hash(key) || hash(leaf_value))
|
|
t.keccak.Write(n.Key)
|
|
kh := t.keccak.Sum(nil)
|
|
t.keccak.Reset()
|
|
|
|
t.keccak.Write(n.Value)
|
|
hash = t.keccak.Sum(nil)
|
|
t.keccak.Reset()
|
|
|
|
t.keccak.Write(kh)
|
|
t.keccak.Write(hash)
|
|
hash = t.keccak.Sum(nil)
|
|
t.keccak.Reset()
|
|
|
|
if t.trace {
|
|
fmt.Printf("leaf %v\n", hex.EncodeToString(hash))
|
|
}
|
|
}
|
|
|
|
//if len(pref) > 1 {
|
|
// fpLen := len(pref) + off
|
|
// t.keccak.Write([]byte{byte(fpLen), byte(fpLen >> 8)})
|
|
// t.keccak.Write(zero30)
|
|
// t.keccak.Write(hash)
|
|
//
|
|
// hash = t.keccak.Sum(nil)
|
|
// t.keccak.Reset()
|
|
//}
|
|
//if t.trace {
|
|
// fmt.Printf("hash %v off %d, pref %d\n", hex.EncodeToString(hash), off, len(pref))
|
|
//}
|
|
n.hash = hash
|
|
|
|
return hash
|
|
}
|
|
|
|
var Zero30 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
|
|
type bitstring []uint8
|
|
|
|
func hexToBin(hex []byte) bitstring {
|
|
bin := make([]byte, 4*len(hex))
|
|
for i := range bin {
|
|
if hex[i/4]&(1<<(3-i%4)) != 0 {
|
|
bin[i] = 1
|
|
}
|
|
}
|
|
return bin
|
|
}
|
|
|
|
func newBitstring(key []byte) bitstring {
|
|
bits := make([]byte, 8*len(key))
|
|
for i := range bits {
|
|
if key[i/8]&(1<<(7-i%7)) != 0 {
|
|
bits[i] = 1
|
|
}
|
|
}
|
|
return bits
|
|
}
|
|
|
|
func bitstringWithPadding(key []byte, _ int) bitstring {
|
|
bs := newBitstring(key)
|
|
if last := key[len(key)-1]; last&0xf0 != 0 {
|
|
padding := int(0xf0 ^ last)
|
|
bs = bs[:len(bs)-8-padding]
|
|
}
|
|
// bs = bs[:len(bs)-padding-1]
|
|
return bs
|
|
}
|
|
|
|
func (b bitstring) String() string {
|
|
var s string
|
|
for _, bit := range b {
|
|
switch bit {
|
|
case 1:
|
|
s += "1"
|
|
case 0:
|
|
s += "0"
|
|
default:
|
|
panic(fmt.Errorf("invalid bit %d in bitstring", bit))
|
|
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (b bitstring) splitPoint(other bitstring) (at int, bit byte, equal bool) {
|
|
for ; at < len(b) && at < len(other); at++ {
|
|
if b[at] != other[at] {
|
|
return at, other[at], false
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case len(b) == len(other):
|
|
return 0, 0, true
|
|
case at == len(b): // b ends before other
|
|
return at, other[at], false
|
|
case at == len(other): // other ends before b
|
|
return at, b[at], false
|
|
default:
|
|
panic("oro")
|
|
}
|
|
}
|
|
|
|
// Converts b into slice of bytes.
|
|
// if len of b is not a multiple of 8, we add 1 <= padding <= 7 zeros to the latest byte
|
|
// and return amount of added zeros
|
|
func (b bitstring) reconstructHex() (re []byte, padding int) {
|
|
re = make([]byte, len(b)/8)
|
|
|
|
var offt, i int
|
|
for {
|
|
bt, ok := b.readByte(offt)
|
|
if !ok {
|
|
break
|
|
}
|
|
re[i] = bt
|
|
offt += 8
|
|
i++
|
|
}
|
|
|
|
if offt >= len(b) {
|
|
return re, 0
|
|
}
|
|
|
|
padding = offt + 8 - len(b)
|
|
pd := append(b[offt:], bytes.Repeat([]byte{0}, padding)...)
|
|
|
|
last, ok := pd.readByte(0)
|
|
if !ok {
|
|
panic(fmt.Errorf("reconstruct failed: padding %d padded size %d", padding, len(pd)))
|
|
}
|
|
pad := byte(padding | 0xf0)
|
|
return append(re, last, pad), padding
|
|
}
|
|
|
|
func (b bitstring) readByte(offsetBits int) (byte, bool) {
|
|
if len(b) <= offsetBits+7 {
|
|
return 0, false
|
|
}
|
|
return b[offsetBits+7] | b[offsetBits+6]<<1 | b[offsetBits+5]<<2 | b[offsetBits+4]<<3 | b[offsetBits+3]<<4 | b[offsetBits+2]<<5 | b[offsetBits+1]<<6 | b[offsetBits]<<7, true
|
|
}
|
|
|
|
// ExtractPlainKeys parses branchData and extract the plain keys for accounts and storage in the same order
|
|
// they appear witjin the branchData
|
|
func ExtractBinPlainKeys(branchData []byte) (accountPlainKeys [][]byte, storagePlainKeys [][]byte, err error) {
|
|
storagePlainKeys = make([][]byte, 0)
|
|
accountPlainKeys = make([][]byte, 0)
|
|
|
|
touchMap := binary.BigEndian.Uint16(branchData[0:])
|
|
afterMap := binary.BigEndian.Uint16(branchData[2:])
|
|
pos := 4
|
|
|
|
for bitset, noop := touchMap&afterMap, 0; bitset != 0; noop++ {
|
|
if pos >= len(branchData) {
|
|
break
|
|
}
|
|
bit := bitset & -bitset
|
|
|
|
fieldBits := PartFlags(branchData[pos])
|
|
pos++
|
|
|
|
if fieldBits&ACCOUNT_PLAIN_PART > 0 {
|
|
n, aux, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("extractBinPlainKeys: [%x] account %w", branchData, err)
|
|
}
|
|
accountPlainKeys = append(accountPlainKeys, aux)
|
|
pos += n
|
|
}
|
|
|
|
if fieldBits&STORAGE_PLAIN_PART > 0 {
|
|
n, aux, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("extractBinPlainKeys: storage %w", err)
|
|
}
|
|
storagePlainKeys = append(storagePlainKeys, aux)
|
|
pos += n
|
|
}
|
|
if fieldBits&HASH_PART > 0 {
|
|
n, _, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("extractBinPlainKeys: hash %w", err)
|
|
}
|
|
pos += n
|
|
}
|
|
bitset ^= bit
|
|
}
|
|
return
|
|
}
|
|
|
|
func decodeSizedBuffer(buf []byte) (int, []byte, error) {
|
|
sz, n := binary.Uvarint(buf)
|
|
switch {
|
|
case n == 0:
|
|
return 0, nil, fmt.Errorf("buffer size too small")
|
|
case n < 0:
|
|
return 0, nil, fmt.Errorf("value overflow")
|
|
default:
|
|
}
|
|
size := int(sz)
|
|
if len(buf) < n+size {
|
|
return n, []byte{}, fmt.Errorf("encoded size larger than buffer size")
|
|
}
|
|
return n + size, buf[n : n+size], nil
|
|
}
|
|
|
|
func ReplaceBinPlainKeys(branchData []byte, accountPlainKeys [][]byte, storagePlainKeys [][]byte, newData []byte) ([]byte, error) {
|
|
var numBuf [binary.MaxVarintLen64]byte
|
|
touchMap := binary.BigEndian.Uint16(branchData[0:])
|
|
afterMap := binary.BigEndian.Uint16(branchData[2:])
|
|
pos := 4
|
|
if cap(newData) < 4 {
|
|
newData = make([]byte, 4)
|
|
}
|
|
copy(newData, branchData[:4])
|
|
|
|
var accountI, storageI int
|
|
for bitset, noop := touchMap&afterMap, 0; bitset != 0; noop++ {
|
|
if pos >= len(branchData) {
|
|
break
|
|
}
|
|
bit := bitset & -bitset
|
|
|
|
fieldBits := PartFlags(branchData[pos])
|
|
newData = append(newData, byte(fieldBits))
|
|
pos++
|
|
|
|
if fieldBits == 0 {
|
|
continue
|
|
}
|
|
|
|
if fieldBits&ACCOUNT_PLAIN_PART > 0 {
|
|
ptr, _, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("replaceBinPlainKeys: account %w", err)
|
|
}
|
|
n := binary.PutUvarint(numBuf[:], uint64(len(accountPlainKeys[accountI])))
|
|
newData = append(newData, numBuf[:n]...)
|
|
newData = append(newData, accountPlainKeys[accountI]...)
|
|
accountI++
|
|
pos += ptr
|
|
}
|
|
if fieldBits&STORAGE_PLAIN_PART > 0 {
|
|
ptr, _, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("replaceBinPlainKeys: storage %w", err)
|
|
}
|
|
n := binary.PutUvarint(numBuf[:], uint64(len(storagePlainKeys[storageI])))
|
|
newData = append(newData, numBuf[:n]...)
|
|
newData = append(newData, storagePlainKeys[storageI]...)
|
|
storageI++
|
|
pos += ptr
|
|
}
|
|
if fieldBits&HASH_PART > 0 {
|
|
n, _, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("extractBinPlainKeys: hash %w", err)
|
|
}
|
|
newData = append(newData, branchData[pos:pos+n]...)
|
|
pos += n
|
|
}
|
|
bitset ^= bit
|
|
}
|
|
return newData, nil
|
|
}
|
|
|
|
func branchToString2(branchData []byte) string {
|
|
if len(branchData) == 0 {
|
|
return "{ DELETED }"
|
|
}
|
|
touchMap := binary.BigEndian.Uint16(branchData[0:])
|
|
afterMap := binary.BigEndian.Uint16(branchData[2:])
|
|
pos := 4
|
|
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "touchMap %016b, afterMap %016b\n", touchMap, afterMap)
|
|
|
|
for bitset, noop := touchMap&afterMap, 0; bitset != 0; noop++ {
|
|
if pos >= len(branchData) {
|
|
break
|
|
}
|
|
bit := bitset & -bitset
|
|
if pos >= len(branchData) {
|
|
break
|
|
}
|
|
|
|
fieldBits := PartFlags(branchData[pos])
|
|
pos++
|
|
|
|
sb.WriteString("{")
|
|
if fieldBits&ACCOUNT_PLAIN_PART > 0 {
|
|
n, pk, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Fprintf(&sb, "accountPlainKey=[%x]", pk)
|
|
pos += n
|
|
|
|
}
|
|
if fieldBits&STORAGE_PLAIN_PART > 0 {
|
|
n, pk, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Fprintf(&sb, "storagePlainKey=[%x]", pk)
|
|
pos += n
|
|
}
|
|
if fieldBits&HASH_PART > 0 {
|
|
n, hash, err := decodeSizedBuffer(branchData[pos:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Fprintf(&sb, " hash=[%x]", hash)
|
|
pos += n
|
|
}
|
|
sb.WriteString(" }\n")
|
|
bitset ^= bit
|
|
}
|
|
return sb.String()
|
|
}
|