mirror of
https://github.com/luxfi/zap.git
synced 2026-07-27 05:54:26 +00:00
283 lines
6.9 KiB
Go
283 lines
6.9 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package zap
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
)
|
|
|
|
// EVM-compatible types for ZAP messages.
|
|
// These types are designed for zero-copy access and EVM interoperability.
|
|
|
|
const (
|
|
// AddressSize is the size of an EVM address (20 bytes)
|
|
AddressSize = 20
|
|
|
|
// HashSize is the size of a keccak256 hash (32 bytes)
|
|
HashSize = 32
|
|
|
|
// SignatureSize is the size of an ECDSA signature (65 bytes: r[32] + s[32] + v[1])
|
|
SignatureSize = 65
|
|
|
|
// BloomSize is the size of a bloom filter (256 bytes)
|
|
BloomSize = 256
|
|
)
|
|
|
|
// Address is a 20-byte EVM address (zero-copy view).
|
|
type Address [AddressSize]byte
|
|
|
|
// Hash is a 32-byte hash (zero-copy view).
|
|
type Hash [HashSize]byte
|
|
|
|
// Signature is a 65-byte ECDSA signature.
|
|
type Signature [SignatureSize]byte
|
|
|
|
// Bloom is a 256-byte bloom filter.
|
|
type Bloom [BloomSize]byte
|
|
|
|
// ZeroAddress is the zero address.
|
|
var ZeroAddress Address
|
|
|
|
// ZeroHash is the zero hash.
|
|
var ZeroHash Hash
|
|
|
|
// AddressFromHex parses an address from hex string.
|
|
func AddressFromHex(s string) (Address, error) {
|
|
if len(s) >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
|
s = s[2:]
|
|
}
|
|
if len(s) != AddressSize*2 {
|
|
return Address{}, fmt.Errorf("invalid address length: %d", len(s))
|
|
}
|
|
var addr Address
|
|
_, err := hex.Decode(addr[:], []byte(s))
|
|
return addr, err
|
|
}
|
|
|
|
// Hex returns the hex-encoded address with 0x prefix.
|
|
func (a Address) Hex() string {
|
|
return "0x" + hex.EncodeToString(a[:])
|
|
}
|
|
|
|
// String implements fmt.Stringer.
|
|
func (a Address) String() string {
|
|
return a.Hex()
|
|
}
|
|
|
|
// IsZero returns true if the address is zero.
|
|
func (a Address) IsZero() bool {
|
|
return a == ZeroAddress
|
|
}
|
|
|
|
// HashFromHex parses a hash from hex string.
|
|
func HashFromHex(s string) (Hash, error) {
|
|
if len(s) >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
|
s = s[2:]
|
|
}
|
|
if len(s) != HashSize*2 {
|
|
return Hash{}, fmt.Errorf("invalid hash length: %d", len(s))
|
|
}
|
|
var h Hash
|
|
_, err := hex.Decode(h[:], []byte(s))
|
|
return h, err
|
|
}
|
|
|
|
// Hex returns the hex-encoded hash with 0x prefix.
|
|
func (h Hash) Hex() string {
|
|
return "0x" + hex.EncodeToString(h[:])
|
|
}
|
|
|
|
// String implements fmt.Stringer.
|
|
func (h Hash) String() string {
|
|
return h.Hex()
|
|
}
|
|
|
|
// IsZero returns true if the hash is zero.
|
|
func (h Hash) IsZero() bool {
|
|
return h == ZeroHash
|
|
}
|
|
|
|
// Bytes32 returns the hash as a 32-byte array.
|
|
func (h Hash) Bytes32() [32]byte {
|
|
return [32]byte(h)
|
|
}
|
|
|
|
// Object extension methods for EVM types
|
|
|
|
// Address reads an address at the given field offset (zero-copy).
|
|
func (o Object) Address(fieldOffset int) Address {
|
|
pos := o.offset + fieldOffset
|
|
if pos+AddressSize > len(o.msg.data) {
|
|
return ZeroAddress
|
|
}
|
|
var addr Address
|
|
copy(addr[:], o.msg.data[pos:pos+AddressSize])
|
|
return addr
|
|
}
|
|
|
|
// Hash reads a hash at the given field offset (zero-copy).
|
|
func (o Object) Hash(fieldOffset int) Hash {
|
|
pos := o.offset + fieldOffset
|
|
if pos+HashSize > len(o.msg.data) {
|
|
return ZeroHash
|
|
}
|
|
var h Hash
|
|
copy(h[:], o.msg.data[pos:pos+HashSize])
|
|
return h
|
|
}
|
|
|
|
// Signature reads a signature at the given field offset.
|
|
func (o Object) Signature(fieldOffset int) Signature {
|
|
pos := o.offset + fieldOffset
|
|
if pos+SignatureSize > len(o.msg.data) {
|
|
return Signature{}
|
|
}
|
|
var sig Signature
|
|
copy(sig[:], o.msg.data[pos:pos+SignatureSize])
|
|
return sig
|
|
}
|
|
|
|
// AddressSlice returns a slice of the address bytes (zero-copy).
|
|
func (o Object) AddressSlice(fieldOffset int) []byte {
|
|
pos := o.offset + fieldOffset
|
|
if pos+AddressSize > len(o.msg.data) {
|
|
return nil
|
|
}
|
|
return o.msg.data[pos : pos+AddressSize]
|
|
}
|
|
|
|
// HashSlice returns a slice of the hash bytes (zero-copy).
|
|
func (o Object) HashSlice(fieldOffset int) []byte {
|
|
pos := o.offset + fieldOffset
|
|
if pos+HashSize > len(o.msg.data) {
|
|
return nil
|
|
}
|
|
return o.msg.data[pos : pos+HashSize]
|
|
}
|
|
|
|
// ObjectBuilder extension methods for EVM types
|
|
|
|
// SetAddress sets an address field.
|
|
func (ob *ObjectBuilder) SetAddress(fieldOffset int, addr Address) {
|
|
ob.ensureField(fieldOffset + AddressSize)
|
|
copy(ob.b.buf[ob.startPos+fieldOffset:], addr[:])
|
|
}
|
|
|
|
// SetHash sets a hash field.
|
|
func (ob *ObjectBuilder) SetHash(fieldOffset int, h Hash) {
|
|
ob.ensureField(fieldOffset + HashSize)
|
|
copy(ob.b.buf[ob.startPos+fieldOffset:], h[:])
|
|
}
|
|
|
|
// SetSignature sets a signature field.
|
|
func (ob *ObjectBuilder) SetSignature(fieldOffset int, sig Signature) {
|
|
ob.ensureField(fieldOffset + SignatureSize)
|
|
copy(ob.b.buf[ob.startPos+fieldOffset:], sig[:])
|
|
}
|
|
|
|
// StructBuilder extension methods for EVM types
|
|
|
|
// Address adds an address field.
|
|
func (sb *StructBuilder) Address(name string) *StructBuilder {
|
|
sb.align(1) // Addresses don't need alignment
|
|
sb.s.Fields = append(sb.s.Fields, Field{Name: name, Type: TypeBytes, Offset: sb.offset})
|
|
sb.offset += AddressSize
|
|
return sb
|
|
}
|
|
|
|
// Hash adds a hash field.
|
|
func (sb *StructBuilder) Hash(name string) *StructBuilder {
|
|
sb.align(1)
|
|
sb.s.Fields = append(sb.s.Fields, Field{Name: name, Type: TypeBytes, Offset: sb.offset})
|
|
sb.offset += HashSize
|
|
return sb
|
|
}
|
|
|
|
// Signature adds a signature field.
|
|
func (sb *StructBuilder) Signature(name string) *StructBuilder {
|
|
sb.align(1)
|
|
sb.s.Fields = append(sb.s.Fields, Field{Name: name, Type: TypeBytes, Offset: sb.offset})
|
|
sb.offset += SignatureSize
|
|
return sb
|
|
}
|
|
|
|
// List extension methods for EVM types
|
|
|
|
// Address returns an address from a list of addresses.
|
|
func (l List) Address(i int) Address {
|
|
if i < 0 || i >= l.length {
|
|
return ZeroAddress
|
|
}
|
|
pos := l.offset + i*AddressSize
|
|
if pos+AddressSize > len(l.msg.data) {
|
|
return ZeroAddress
|
|
}
|
|
var addr Address
|
|
copy(addr[:], l.msg.data[pos:pos+AddressSize])
|
|
return addr
|
|
}
|
|
|
|
// Hash returns a hash from a list of hashes.
|
|
func (l List) Hash(i int) Hash {
|
|
if i < 0 || i >= l.length {
|
|
return ZeroHash
|
|
}
|
|
pos := l.offset + i*HashSize
|
|
if pos+HashSize > len(l.msg.data) {
|
|
return ZeroHash
|
|
}
|
|
var h Hash
|
|
copy(h[:], l.msg.data[pos:pos+HashSize])
|
|
return h
|
|
}
|
|
|
|
// Common EVM message schemas
|
|
|
|
// TransactionSchema defines the schema for an EVM transaction.
|
|
var TransactionSchema = NewStructBuilder("Transaction").
|
|
Hash("hash").
|
|
Uint64("nonce").
|
|
Address("from").
|
|
Address("to").
|
|
Bytes("value"). // uint256 as bytes
|
|
Bytes("data").
|
|
Uint64("gas").
|
|
Bytes("gasPrice"). // uint256 as bytes
|
|
Uint64("chainId").
|
|
Signature("signature").
|
|
Build()
|
|
|
|
// BlockHeaderSchema defines the schema for an EVM block header.
|
|
var BlockHeaderSchema = NewStructBuilder("BlockHeader").
|
|
Hash("parentHash").
|
|
Hash("uncleHash").
|
|
Address("coinbase").
|
|
Hash("stateRoot").
|
|
Hash("transactionsRoot").
|
|
Hash("receiptsRoot").
|
|
Bytes("logsBloom"). // 256 bytes
|
|
Uint64("difficulty").
|
|
Uint64("number").
|
|
Uint64("gasLimit").
|
|
Uint64("gasUsed").
|
|
Uint64("timestamp").
|
|
Bytes("extraData").
|
|
Hash("mixHash").
|
|
Uint64("nonce").
|
|
Build()
|
|
|
|
// LogSchema defines the schema for an EVM log entry.
|
|
var LogSchema = NewStructBuilder("Log").
|
|
Address("address").
|
|
List("topics", TypeBytes). // []Hash
|
|
Bytes("data").
|
|
Uint64("blockNumber").
|
|
Hash("txHash").
|
|
Uint32("txIndex").
|
|
Hash("blockHash").
|
|
Uint32("logIndex").
|
|
Bool("removed").
|
|
Build()
|