Files
mev-beta/vendor/github.com/ethereum/go-verkle/encoding.go

222 lines
8.5 KiB
Go

// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <https://unlicense.org>
package verkle
import (
"errors"
"fmt"
"github.com/crate-crypto/go-ipa/banderwagon"
)
var (
ErrInvalidNodeEncoding = errors.New("invalid node encoding")
mask = [8]byte{0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}
)
const (
nodeTypeSize = 1
bitlistSize = NodeWidth / 8
// Shared between internal and leaf nodes.
nodeTypeOffset = 0
// Internal nodes offsets.
internalBitlistOffset = nodeTypeOffset + nodeTypeSize
internalCommitmentOffset = internalBitlistOffset + bitlistSize
// Leaf node offsets.
leafStemOffset = nodeTypeOffset + nodeTypeSize
leafBitlistOffset = leafStemOffset + StemSize
leafCommitmentOffset = leafBitlistOffset + bitlistSize
leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize
leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize
leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize
leafBasicDataSize = 32
leafSlotSize = 32
leafValueIndexSize = 1
singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize
eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize
)
func bit(bitlist []byte, nr int) bool {
if len(bitlist)*8 <= nr {
return false
}
return bitlist[nr/8]&mask[nr%8] != 0
}
var errSerializedPayloadTooShort = errors.New("verkle payload is too short")
// ParseNode deserializes a node into its proper VerkleNode instance.
// The serialized bytes have the format:
// - Internal nodes: <nodeType><bitlist><commitment>
// - Leaf nodes: <nodeType><stem><bitlist><comm><c1comm><c2comm><children...>
// - EoA nodes: <nodeType><stem><comm><c1comm><balance><nonce>
// - single slot node: <nodeType><stem><comm><cncomm><leaf index><slot>
func ParseNode(serializedNode []byte, depth byte) (VerkleNode, error) {
// Check that the length of the serialized node is at least the smallest possible serialized node.
if len(serializedNode) < nodeTypeSize+banderwagon.UncompressedSize {
return nil, errSerializedPayloadTooShort
}
switch serializedNode[0] {
case leafType:
return parseLeafNode(serializedNode, depth)
case internalType:
return CreateInternalNode(serializedNode[internalBitlistOffset:internalCommitmentOffset], serializedNode[internalCommitmentOffset:], depth)
case eoAccountType:
return parseEoAccountNode(serializedNode, depth)
case singleSlotType:
return parseSingleSlotNode(serializedNode, depth)
default:
return nil, ErrInvalidNodeEncoding
}
}
func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) {
bitlist := serialized[leafBitlistOffset : leafBitlistOffset+bitlistSize]
var values [NodeWidth][]byte
offset := leafChildrenOffset
for i := 0; i < NodeWidth; i++ {
if bit(bitlist, i) {
if offset+LeafValueSize > len(serialized) {
return nil, fmt.Errorf("verkle payload is too short, need at least %d and only have %d, payload = %x (%w)", offset+LeafValueSize, len(serialized), serialized, errSerializedPayloadTooShort)
}
values[i] = serialized[offset : offset+LeafValueSize]
offset += LeafValueSize
}
}
ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:])
ln.setDepth(depth)
ln.c1 = new(Point)
// Sanity check that we have at least 3*banderwagon.UncompressedSize bytes left in the serialized payload.
if len(serialized[leafCommitmentOffset:]) < 3*banderwagon.UncompressedSize {
return nil, fmt.Errorf("leaf node commitments are not the correct size, expected at least %d, got %d", 3*banderwagon.UncompressedSize, len(serialized[leafC1CommitmentOffset:]))
}
if err := ln.c1.SetBytesUncompressed(serialized[leafC1CommitmentOffset:leafC1CommitmentOffset+banderwagon.UncompressedSize], true); err != nil {
return nil, fmt.Errorf("setting c1 commitment: %w", err)
}
ln.c2 = new(Point)
if err := ln.c2.SetBytesUncompressed(serialized[leafC2CommitmentOffset:leafC2CommitmentOffset+banderwagon.UncompressedSize], true); err != nil {
return nil, fmt.Errorf("setting c2 commitment: %w", err)
}
ln.commitment = new(Point)
if err := ln.commitment.SetBytesUncompressed(serialized[leafCommitmentOffset:leafC1CommitmentOffset], true); err != nil {
return nil, fmt.Errorf("setting commitment: %w", err)
}
return ln, nil
}
func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) {
var values [NodeWidth][]byte
offset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize
values[0] = serialized[offset : offset+leafBasicDataSize] // basic data
values[1] = EmptyCodeHash[:]
ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:])
ln.setDepth(depth)
ln.c1 = new(Point)
if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil {
return nil, fmt.Errorf("error setting leaf C1 commitment: %w", err)
}
ln.c2 = &banderwagon.Identity
ln.commitment = new(Point)
if err := ln.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize+banderwagon.UncompressedSize:leafStemOffset+StemSize+banderwagon.UncompressedSize*2], true); err != nil {
return nil, fmt.Errorf("error setting leaf root commitment: %w", err)
}
return ln, nil
}
func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) {
var values [NodeWidth][]byte
offset := leafStemOffset
ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:])
offset += StemSize
cnCommBytes := serialized[offset : offset+banderwagon.UncompressedSize]
offset += banderwagon.UncompressedSize
rootCommBytes := serialized[offset : offset+banderwagon.UncompressedSize]
offset += banderwagon.UncompressedSize
idx := serialized[offset]
offset += leafValueIndexSize
values[idx] = serialized[offset : offset+leafSlotSize] // copy slot
ln.setDepth(depth)
if idx < 128 {
ln.c1 = new(Point)
if err := ln.c1.SetBytesUncompressed(cnCommBytes, true); err != nil {
return nil, fmt.Errorf("error setting leaf C1 commitment: %w", err)
}
ln.c2 = &banderwagon.Identity
} else {
ln.c2 = new(Point)
if err := ln.c2.SetBytesUncompressed(cnCommBytes, true); err != nil {
return nil, fmt.Errorf("error setting leaf C2 commitment: %w", err)
}
ln.c1 = &banderwagon.Identity
}
ln.commitment = new(Point)
if err := ln.commitment.SetBytesUncompressed(rootCommBytes, true); err != nil {
return nil, fmt.Errorf("error setting leaf root commitment: %w", err)
}
return ln, nil
}
func CreateInternalNode(bitlist []byte, raw []byte, depth byte) (*InternalNode, error) {
// GetTreeConfig caches computation result, hence
// this op has low overhead
node := new(InternalNode)
if len(bitlist) != bitlistSize {
return nil, ErrInvalidNodeEncoding
}
// Create a HashNode placeholder for all values
// corresponding to a set bit.
node.children = make([]VerkleNode, NodeWidth)
for i, b := range bitlist {
for j := 0; j < 8; j++ {
if b&mask[j] != 0 {
node.children[8*i+j] = HashedNode{}
} else {
node.children[8*i+j] = Empty(struct{}{})
}
}
}
node.depth = depth
if len(raw) != banderwagon.UncompressedSize {
return nil, ErrInvalidNodeEncoding
}
node.commitment = new(Point)
if err := node.commitment.SetBytesUncompressed(raw, true); err != nil {
return nil, fmt.Errorf("setting commitment: %w", err)
}
return node, nil
}