Add ZAP wire protocol transport for VM communication

- Add zap/ package with zero-copy binary wire protocol
- Support for VM client/server communication over ZAP
- Wire format: [4-byte length][1-byte message type][payload]
- Includes sender, transport, and VM implementations
This commit is contained in:
2026-01-24 11:39:40 -08:00
parent b1db41a415
commit dd2f2c716e
11 changed files with 2672 additions and 2 deletions
+10
View File
@@ -27,4 +27,14 @@ type Service interface {
SetTrackedChains(ctx context.Context, args *SetTrackedChainsArgs) (*SetTrackedChainsReply, error)
GetTrackedChains(ctx context.Context) (*GetTrackedChainsReply, error)
// Snapshot creates a database backup to the specified path.
// Use .zst extension for zstd compression.
// since: 0 for full backup, or version from last backup for incremental.
// Returns the version number for future incremental backups.
Snapshot(ctx context.Context, args *SnapshotArgs) (*SnapshotReply, error)
// Load restores the database from a backup file.
// Detects compression from .zst extension.
Load(ctx context.Context, args *LoadArgs) (*EmptyReply, error)
}
+26 -2
View File
@@ -2,12 +2,36 @@ module github.com/luxfi/api
go 1.25.5
require github.com/luxfi/ids v1.2.9
require (
capnproto.org/go/capnp/v3 v3.1.0-alpha.2
github.com/cloudflare/circl v1.6.2
github.com/luxfi/crypto v1.17.38
github.com/luxfi/formatting v1.0.1
github.com/luxfi/ids v1.2.9
github.com/luxfi/math v1.2.3
google.golang.org/grpc v1.78.0
)
require (
github.com/luxfi/crypto v1.17.38 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251230134950-44c893854e3f // indirect
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/luxfi/cache v1.1.0 // indirect
github.com/luxfi/math/big v0.1.0 // indirect
github.com/luxfi/mock v0.1.0 // indirect
github.com/luxfi/sampler v1.0.0 // indirect
github.com/luxfi/utils v1.1.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect
go.uber.org/mock v0.6.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
+39
View File
@@ -1,20 +1,59 @@
capnproto.org/go/capnp/v3 v3.1.0-alpha.2 h1:93ISpqHf2/3WQlfrBP0tT8Dg/RQc3uq6DV36vN0ePWk=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251230134950-44c893854e3f h1:a5PUgHGinaD6XrLmIDLQmGHocjIjBsBAcR5gALjZvMU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 h1:d5EKgQfRQvO97jnISfR89AiCCCJMwMFoSxUiU0OGCRU=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/luxfi/cache v1.1.0 h1:6LUyGGZ+rrMAJBbAU6+UwkcamXj3zsboRUodIof2Ong=
github.com/luxfi/constants v1.4.3 h1:bcqUO8twMOhHWCgN/RbjfWulVPbvUQcxDkssaRrCx4g=
github.com/luxfi/crypto v1.17.38 h1:PZ52opsm3ECvyKsR2pLSsKONCey+FqpN0ZEwu+KMdO4=
github.com/luxfi/crypto v1.17.38/go.mod h1:G2t1GQvPsrwnzwyVEj0LQDuX2AWZVI5kEAPyVeicc5o=
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
github.com/luxfi/geth v1.16.69 h1:CHO6xTZ+A+3itk94ts4uyVRJajNVP3RxWTjJp5qGOlk=
github.com/luxfi/ids v1.2.9 h1:+yjdhXW99drnd2Zlp1u/p8k3G23W3/1btJQ4ogHawUI=
github.com/luxfi/ids v1.2.9/go.mod h1:khJOEdOPxd22yn0jcVrnbX1ADa0GHn5Y74gvCzN5BYc=
github.com/luxfi/math v1.2.3 h1:BgvIFw/srPXFLbcqtoDhLJOfmBsn86GPA1iWgsoyUb4=
github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
github.com/luxfi/mock v0.1.0 h1:IwElfNu+T9sXvzFX6tudPDx1vqPuACRSRdxpD5lxW+o=
github.com/luxfi/mock v0.1.0/go.mod h1:izF+9K0gGzFC9zERn6Po37v46eLdPB+EIsDjL3GLk+U=
github.com/luxfi/sampler v1.0.0 h1:k8Sf6otW83w4pQp0jXLA+g3J/joB7w7SqXQsWmNTOV0=
github.com/luxfi/utils v1.1.0 h1:ti7HvjNwJd4ILDMERJtOAWE9mF8l+zqDVkgWnF7Agic=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU=
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+17
View File
@@ -0,0 +1,17 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package zap provides Zero-Copy App Proto (ZAP) serialization for high-performance
// RPC communication. ZAP minimizes CPU overhead and memory allocations through:
//
// - Direct memory access for fixed-size fields
// - Length-prefixed variable data without copying
// - Buffer pooling to avoid allocations
// - Simple framing for TCP transport (no HTTP/2 overhead)
//
// Wire protocol:
//
// [4 bytes: message length][1 byte: message type][payload...]
//
// The message type allows multiplexing multiple RPC calls over a single connection.
package zap
+145
View File
@@ -0,0 +1,145 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
// SendRequestMsg contains request to send to nodes (p2p.Sender.SendRequest)
type SendRequestMsg struct {
NodeIDs [][]byte
RequestID uint32
Request []byte // Zero-copy payload
}
// Encode serializes SendRequestMsg to the buffer
func (m *SendRequestMsg) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(m.NodeIDs)))
for _, nodeID := range m.NodeIDs {
buf.WriteBytes(nodeID)
}
buf.WriteUint32(m.RequestID)
buf.WriteBytes(m.Request)
}
// Decode deserializes SendRequestMsg from the reader
func (m *SendRequestMsg) Decode(r *Reader) error {
count, err := r.ReadUint32()
if err != nil {
return err
}
m.NodeIDs = make([][]byte, count)
for i := uint32(0); i < count; i++ {
if m.NodeIDs[i], err = r.ReadBytes(); err != nil {
return err
}
}
if m.RequestID, err = r.ReadUint32(); err != nil {
return err
}
m.Request, err = r.ReadBytes()
return err
}
// SendResponseMsg contains response to send to a node (p2p.Sender.SendResponse)
type SendResponseMsg struct {
NodeID []byte
RequestID uint32
Response []byte // Zero-copy payload
}
// Encode serializes SendResponseMsg to the buffer
func (m *SendResponseMsg) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteUint32(m.RequestID)
buf.WriteBytes(m.Response)
}
// Decode deserializes SendResponseMsg from the reader
func (m *SendResponseMsg) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.RequestID, err = r.ReadUint32(); err != nil {
return err
}
m.Response, err = r.ReadBytes()
return err
}
// SendErrorMsg contains error to send to a node (p2p.Sender.SendError)
type SendErrorMsg struct {
NodeID []byte
RequestID uint32
ErrorCode int32
ErrorMessage string
}
// Encode serializes SendErrorMsg to the buffer
func (m *SendErrorMsg) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteUint32(m.RequestID)
buf.WriteInt32(m.ErrorCode)
buf.WriteString(m.ErrorMessage)
}
// Decode deserializes SendErrorMsg from the reader
func (m *SendErrorMsg) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.RequestID, err = r.ReadUint32(); err != nil {
return err
}
if m.ErrorCode, err = r.ReadInt32(); err != nil {
return err
}
m.ErrorMessage, err = r.ReadString()
return err
}
// SendGossipMsg contains gossip message to send (p2p.Sender.SendGossip)
type SendGossipMsg struct {
NodeIDs [][]byte
Validators uint64
NonValidators uint64
Peers uint64
Msg []byte // Zero-copy gossip payload
}
// Encode serializes SendGossipMsg to the buffer
func (m *SendGossipMsg) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(m.NodeIDs)))
for _, nodeID := range m.NodeIDs {
buf.WriteBytes(nodeID)
}
buf.WriteUint64(m.Validators)
buf.WriteUint64(m.NonValidators)
buf.WriteUint64(m.Peers)
buf.WriteBytes(m.Msg)
}
// Decode deserializes SendGossipMsg from the reader
func (m *SendGossipMsg) Decode(r *Reader) error {
count, err := r.ReadUint32()
if err != nil {
return err
}
m.NodeIDs = make([][]byte, count)
for i := uint32(0); i < count; i++ {
if m.NodeIDs[i], err = r.ReadBytes(); err != nil {
return err
}
}
if m.Validators, err = r.ReadUint64(); err != nil {
return err
}
if m.NonValidators, err = r.ReadUint64(); err != nil {
return err
}
if m.Peers, err = r.ReadUint64(); err != nil {
return err
}
m.Msg, err = r.ReadBytes()
return err
}
+477
View File
@@ -0,0 +1,477 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
import (
"bufio"
"context"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"sync"
"sync/atomic"
"time"
)
var (
ErrClosed = errors.New("zap: connection closed")
ErrTimeout = errors.New("zap: request timeout")
ErrResponseFailed = errors.New("zap: response failed")
)
// Config contains transport configuration
type Config struct {
// ReadTimeout is the timeout for reading a message
ReadTimeout time.Duration
// WriteTimeout is the timeout for writing a message
WriteTimeout time.Duration
// MaxConcurrent is the maximum number of concurrent requests
MaxConcurrent int
// BufferSize is the read/write buffer size
BufferSize int
}
// DefaultConfig returns a Config with reasonable defaults
func DefaultConfig() *Config {
return &Config{
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Second,
MaxConcurrent: 1000,
BufferSize: 64 * 1024,
}
}
// Conn is a ZAP connection that multiplexes requests
type Conn struct {
conn net.Conn
config *Config
reader *bufio.Reader
writer *bufio.Writer
mu sync.Mutex
requests map[uint32]chan *response
nextID uint32
closed atomic.Bool
done chan struct{}
}
type response struct {
msgType MessageType
payload []byte
err error
}
// Dial connects to a ZAP server
func Dial(ctx context.Context, addr string, config *Config) (*Conn, error) {
if config == nil {
config = DefaultConfig()
}
dialer := &net.Dialer{}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, fmt.Errorf("zap: dial failed: %w", err)
}
return newConn(conn, config), nil
}
// NewConn wraps an existing net.Conn as a ZAP connection
func NewConn(conn net.Conn, config *Config) *Conn {
if config == nil {
config = DefaultConfig()
}
return newConn(conn, config)
}
func newConn(conn net.Conn, config *Config) *Conn {
c := &Conn{
conn: conn,
config: config,
reader: bufio.NewReaderSize(conn, config.BufferSize),
writer: bufio.NewWriterSize(conn, config.BufferSize),
requests: make(map[uint32]chan *response),
done: make(chan struct{}),
}
go c.readLoop()
return c
}
// Close closes the connection
func (c *Conn) Close() error {
if c.closed.Swap(true) {
return nil
}
close(c.done)
c.mu.Lock()
for _, ch := range c.requests {
ch <- &response{err: ErrClosed}
}
c.requests = nil
c.mu.Unlock()
return c.conn.Close()
}
// Call sends a request and waits for a response
func (c *Conn) Call(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error) {
if c.closed.Load() {
return 0, nil, ErrClosed
}
// Allocate request ID and response channel
id := atomic.AddUint32(&c.nextID, 1)
respCh := make(chan *response, 1)
c.mu.Lock()
if c.requests == nil {
c.mu.Unlock()
return 0, nil, ErrClosed
}
c.requests[id] = respCh
c.mu.Unlock()
defer func() {
c.mu.Lock()
delete(c.requests, id)
c.mu.Unlock()
}()
// Write request with request ID prefix
reqBuf := GetBuffer()
defer PutBuffer(reqBuf)
reqBuf.WriteUint32(id)
reqBuf.Grow(reqBuf.Len() + len(payload))
copy(reqBuf.Data[reqBuf.offset:], payload)
reqBuf.offset += len(payload)
if err := c.write(msgType, reqBuf.Bytes()); err != nil {
return 0, nil, err
}
// Wait for response
select {
case <-ctx.Done():
return 0, nil, ctx.Err()
case <-c.done:
return 0, nil, ErrClosed
case resp := <-respCh:
if resp.err != nil {
return 0, nil, resp.err
}
return resp.msgType, resp.payload, nil
}
}
// Send sends a one-way message (no response expected)
func (c *Conn) Send(msgType MessageType, payload []byte) error {
if c.closed.Load() {
return ErrClosed
}
return c.write(msgType, payload)
}
func (c *Conn) write(msgType MessageType, payload []byte) error {
c.mu.Lock()
defer c.mu.Unlock()
if c.config.WriteTimeout > 0 {
_ = c.conn.SetWriteDeadline(time.Now().Add(c.config.WriteTimeout))
}
if err := WriteMessage(c.writer, msgType, payload); err != nil {
return err
}
return c.writer.Flush()
}
func (c *Conn) readLoop() {
for {
if c.config.ReadTimeout > 0 {
_ = c.conn.SetReadDeadline(time.Now().Add(c.config.ReadTimeout))
}
msgType, payload, err := ReadMessage(c.reader)
if err != nil {
if !c.closed.Load() {
c.Close()
}
return
}
// Extract request ID from response
if len(payload) < 4 {
continue
}
reqID := binary.BigEndian.Uint32(payload[:4])
respPayload := payload[4:]
c.mu.Lock()
ch, ok := c.requests[reqID]
c.mu.Unlock()
if ok {
ch <- &response{
msgType: msgType,
payload: respPayload,
}
}
}
}
// Listener accepts ZAP connections
type Listener struct {
listener net.Listener
config *Config
}
// Listen creates a new ZAP listener
func Listen(addr string, config *Config) (*Listener, error) {
if config == nil {
config = DefaultConfig()
}
listener, err := net.Listen("tcp", addr)
if err != nil {
return nil, fmt.Errorf("zap: listen failed: %w", err)
}
return &Listener{
listener: listener,
config: config,
}, nil
}
// Accept accepts a new connection
func (l *Listener) Accept() (*ServerConn, error) {
conn, err := l.listener.Accept()
if err != nil {
return nil, err
}
return newServerConn(conn, l.config), nil
}
// Close closes the listener
func (l *Listener) Close() error {
return l.listener.Close()
}
// Addr returns the listener's address
func (l *Listener) Addr() net.Addr {
return l.listener.Addr()
}
// ServerConn is a server-side ZAP connection
type ServerConn struct {
conn net.Conn
config *Config
reader *bufio.Reader
writer *bufio.Writer
writeMu sync.Mutex
closed atomic.Bool
}
func newServerConn(conn net.Conn, config *Config) *ServerConn {
return &ServerConn{
conn: conn,
config: config,
reader: bufio.NewReaderSize(conn, config.BufferSize),
writer: bufio.NewWriterSize(conn, config.BufferSize),
}
}
// Read reads the next request from the connection
func (c *ServerConn) Read() (uint32, MessageType, []byte, error) {
if c.closed.Load() {
return 0, 0, nil, ErrClosed
}
if c.config.ReadTimeout > 0 {
_ = c.conn.SetReadDeadline(time.Now().Add(c.config.ReadTimeout))
}
msgType, payload, err := ReadMessage(c.reader)
if err != nil {
return 0, 0, nil, err
}
// Extract request ID
if len(payload) < 4 {
return 0, 0, nil, ErrInvalidMessage
}
reqID := binary.BigEndian.Uint32(payload[:4])
return reqID, msgType, payload[4:], nil
}
// Write writes a response
func (c *ServerConn) Write(reqID uint32, msgType MessageType, payload []byte) error {
if c.closed.Load() {
return ErrClosed
}
// Prepend request ID to response
respBuf := GetBuffer()
defer PutBuffer(respBuf)
respBuf.WriteUint32(reqID)
respBuf.Grow(respBuf.Len() + len(payload))
copy(respBuf.Data[respBuf.offset:], payload)
respBuf.offset += len(payload)
c.writeMu.Lock()
defer c.writeMu.Unlock()
if c.config.WriteTimeout > 0 {
_ = c.conn.SetWriteDeadline(time.Now().Add(c.config.WriteTimeout))
}
if err := WriteMessage(c.writer, msgType|MsgResponseFlag, respBuf.Bytes()); err != nil {
return err
}
return c.writer.Flush()
}
// Close closes the server connection
func (c *ServerConn) Close() error {
if c.closed.Swap(true) {
return nil
}
return c.conn.Close()
}
// RemoteAddr returns the remote address
func (c *ServerConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
// Handler processes ZAP requests
type Handler interface {
// Handle processes a request and returns a response
Handle(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error)
}
// HandlerFunc is a function that implements Handler
type HandlerFunc func(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error)
// Handle implements Handler
func (f HandlerFunc) Handle(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error) {
return f(ctx, msgType, payload)
}
// Server serves ZAP requests
type Server struct {
listener *Listener
handler Handler
mu sync.Mutex
conns map[*ServerConn]struct{}
closed atomic.Bool
done chan struct{}
}
// NewServer creates a new ZAP server
func NewServer(listener *Listener, handler Handler) *Server {
return &Server{
listener: listener,
handler: handler,
conns: make(map[*ServerConn]struct{}),
done: make(chan struct{}),
}
}
// Serve accepts and processes connections
func (s *Server) Serve(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-s.done:
return nil
default:
}
conn, err := s.listener.Accept()
if err != nil {
if s.closed.Load() {
return nil
}
continue
}
s.mu.Lock()
s.conns[conn] = struct{}{}
s.mu.Unlock()
go s.handleConn(ctx, conn)
}
}
func (s *Server) handleConn(ctx context.Context, conn *ServerConn) {
defer func() {
conn.Close()
s.mu.Lock()
delete(s.conns, conn)
s.mu.Unlock()
}()
for {
select {
case <-ctx.Done():
return
case <-s.done:
return
default:
}
reqID, msgType, payload, err := conn.Read()
if err != nil {
if err == io.EOF || errors.Is(err, net.ErrClosed) {
return
}
continue
}
// Handle request
respType, respPayload, err := s.handler.Handle(ctx, msgType, payload)
if err != nil {
// Send error response
errBuf := GetBuffer()
errBuf.WriteString(err.Error())
_ = conn.Write(reqID, msgType|MsgResponseFlag, errBuf.Bytes())
PutBuffer(errBuf)
continue
}
// Send response
if err := conn.Write(reqID, respType, respPayload); err != nil {
return
}
}
}
// Close closes the server
func (s *Server) Close() error {
if s.closed.Swap(true) {
return nil
}
close(s.done)
s.mu.Lock()
for conn := range s.conns {
conn.Close()
}
s.conns = nil
s.mu.Unlock()
return s.listener.Close()
}
+214
View File
@@ -0,0 +1,214 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
import (
"bytes"
"context"
"sync"
"testing"
"time"
)
func TestTransport(t *testing.T) {
// Start server
listener, err := Listen("127.0.0.1:0", nil)
if err != nil {
t.Fatalf("Listen: %v", err)
}
defer listener.Close()
// Echo handler - returns the same message type and payload
handler := HandlerFunc(func(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error) {
return msgType, payload, nil
})
server := NewServer(listener, handler)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
_ = server.Serve(ctx)
}()
// Give server time to start
time.Sleep(10 * time.Millisecond)
// Connect client
conn, err := Dial(ctx, listener.Addr().String(), nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer conn.Close()
// Test request/response
payload := []byte("test payload")
respType, respPayload, err := conn.Call(ctx, MsgBuildBlock, payload)
if err != nil {
t.Fatalf("Call: %v", err)
}
if respType != MsgBuildBlock|MsgResponseFlag {
t.Errorf("Expected response type %d, got %d", MsgBuildBlock|MsgResponseFlag, respType)
}
if !bytes.Equal(respPayload, payload) {
t.Errorf("Payload mismatch: got %v, want %v", respPayload, payload)
}
// Clean up
cancel()
server.Close()
wg.Wait()
}
func TestConcurrentCalls(t *testing.T) {
// Start server
listener, err := Listen("127.0.0.1:0", nil)
if err != nil {
t.Fatalf("Listen: %v", err)
}
defer listener.Close()
// Echo handler with small delay
handler := HandlerFunc(func(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error) {
return msgType, payload, nil
})
server := NewServer(listener, handler)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_ = server.Serve(ctx)
}()
time.Sleep(10 * time.Millisecond)
// Connect client
conn, err := Dial(ctx, listener.Addr().String(), nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer conn.Close()
// Launch concurrent calls
const numCalls = 100
var wg sync.WaitGroup
errors := make(chan error, numCalls)
for i := 0; i < numCalls; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
payload := []byte{byte(i)}
_, respPayload, err := conn.Call(ctx, MsgParseBlock, payload)
if err != nil {
errors <- err
return
}
if !bytes.Equal(respPayload, payload) {
errors <- ErrResponseFailed
}
}(i)
}
wg.Wait()
close(errors)
for err := range errors {
t.Errorf("Concurrent call error: %v", err)
}
cancel()
server.Close()
}
func BenchmarkRoundTrip(b *testing.B) {
listener, err := Listen("127.0.0.1:0", nil)
if err != nil {
b.Fatalf("Listen: %v", err)
}
defer listener.Close()
handler := HandlerFunc(func(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error) {
return msgType, payload, nil
})
server := NewServer(listener, handler)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_ = server.Serve(ctx)
}()
time.Sleep(10 * time.Millisecond)
conn, err := Dial(ctx, listener.Addr().String(), nil)
if err != nil {
b.Fatalf("Dial: %v", err)
}
defer conn.Close()
payload := make([]byte, 1024)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := conn.Call(ctx, MsgBuildBlock, payload)
if err != nil {
b.Fatalf("Call: %v", err)
}
}
b.SetBytes(int64(len(payload)))
cancel()
server.Close()
}
func BenchmarkRoundTripLargeBlock(b *testing.B) {
listener, err := Listen("127.0.0.1:0", nil)
if err != nil {
b.Fatalf("Listen: %v", err)
}
defer listener.Close()
handler := HandlerFunc(func(ctx context.Context, msgType MessageType, payload []byte) (MessageType, []byte, error) {
return msgType, payload, nil
})
server := NewServer(listener, handler)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_ = server.Serve(ctx)
}()
time.Sleep(10 * time.Millisecond)
conn, err := Dial(ctx, listener.Addr().String(), nil)
if err != nil {
b.Fatalf("Dial: %v", err)
}
defer conn.Close()
// Simulate 100KB block
payload := make([]byte, 100*1024)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := conn.Call(ctx, MsgBuildBlock, payload)
if err != nil {
b.Fatalf("Call: %v", err)
}
}
b.SetBytes(int64(len(payload)))
cancel()
server.Close()
}
+936
View File
@@ -0,0 +1,936 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
// State represents the VM state
type State uint8
const (
StateUnspecified State = iota
StateStateSyncing
StateBootstrapping
StateNormalOp
)
// Error represents VM errors
type Error uint8
const (
ErrorUnspecified Error = iota
ErrorClosed
ErrorNotFound
ErrorStateSyncNotImplemented
)
// NetworkUpgrades contains network upgrade timestamps
type NetworkUpgrades struct {
ApricotPhase1Time int64
ApricotPhase2Time int64
ApricotPhase3Time int64
ApricotPhase4Time int64
ApricotPhase4MinPChainHeight uint64
ApricotPhase5Time int64
ApricotPhasePre6Time int64
ApricotPhase6Time int64
ApricotPhasePost6Time int64
BanffTime int64
CortinaTime int64
CortinaXChainStopVertexID []byte
DurangoTime int64
EtnaTime int64
FortunaTime int64
GraniteTime int64
}
// Encode serializes NetworkUpgrades to the buffer
func (n *NetworkUpgrades) Encode(buf *Buffer) {
buf.WriteInt64(n.ApricotPhase1Time)
buf.WriteInt64(n.ApricotPhase2Time)
buf.WriteInt64(n.ApricotPhase3Time)
buf.WriteInt64(n.ApricotPhase4Time)
buf.WriteUint64(n.ApricotPhase4MinPChainHeight)
buf.WriteInt64(n.ApricotPhase5Time)
buf.WriteInt64(n.ApricotPhasePre6Time)
buf.WriteInt64(n.ApricotPhase6Time)
buf.WriteInt64(n.ApricotPhasePost6Time)
buf.WriteInt64(n.BanffTime)
buf.WriteInt64(n.CortinaTime)
buf.WriteBytes(n.CortinaXChainStopVertexID)
buf.WriteInt64(n.DurangoTime)
buf.WriteInt64(n.EtnaTime)
buf.WriteInt64(n.FortunaTime)
buf.WriteInt64(n.GraniteTime)
}
// Decode deserializes NetworkUpgrades from the reader
func (n *NetworkUpgrades) Decode(r *Reader) error {
var err error
if n.ApricotPhase1Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhase2Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhase3Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhase4Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhase4MinPChainHeight, err = r.ReadUint64(); err != nil {
return err
}
if n.ApricotPhase5Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhasePre6Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhase6Time, err = r.ReadInt64(); err != nil {
return err
}
if n.ApricotPhasePost6Time, err = r.ReadInt64(); err != nil {
return err
}
if n.BanffTime, err = r.ReadInt64(); err != nil {
return err
}
if n.CortinaTime, err = r.ReadInt64(); err != nil {
return err
}
if n.CortinaXChainStopVertexID, err = r.ReadBytes(); err != nil {
return err
}
if n.DurangoTime, err = r.ReadInt64(); err != nil {
return err
}
if n.EtnaTime, err = r.ReadInt64(); err != nil {
return err
}
if n.FortunaTime, err = r.ReadInt64(); err != nil {
return err
}
n.GraniteTime, err = r.ReadInt64()
return err
}
// InitializeRequest contains initialization parameters
type InitializeRequest struct {
NetworkID uint32
ChainID []byte
NodeID []byte
PublicKey []byte
XChainID []byte
CChainID []byte
LuxAssetID []byte
ChainDataDir string
GenesisBytes []byte
UpgradeBytes []byte
ConfigBytes []byte
DBServerAddr string
ServerAddr string
NetworkUpgrades NetworkUpgrades
}
// Encode serializes InitializeRequest to the buffer
func (m *InitializeRequest) Encode(buf *Buffer) {
buf.WriteUint32(m.NetworkID)
buf.WriteBytes(m.ChainID)
buf.WriteBytes(m.NodeID)
buf.WriteBytes(m.PublicKey)
buf.WriteBytes(m.XChainID)
buf.WriteBytes(m.CChainID)
buf.WriteBytes(m.LuxAssetID)
buf.WriteString(m.ChainDataDir)
buf.WriteBytes(m.GenesisBytes)
buf.WriteBytes(m.UpgradeBytes)
buf.WriteBytes(m.ConfigBytes)
buf.WriteString(m.DBServerAddr)
buf.WriteString(m.ServerAddr)
m.NetworkUpgrades.Encode(buf)
}
// Decode deserializes InitializeRequest from the reader
func (m *InitializeRequest) Decode(r *Reader) error {
var err error
if m.NetworkID, err = r.ReadUint32(); err != nil {
return err
}
if m.ChainID, err = r.ReadBytes(); err != nil {
return err
}
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.PublicKey, err = r.ReadBytes(); err != nil {
return err
}
if m.XChainID, err = r.ReadBytes(); err != nil {
return err
}
if m.CChainID, err = r.ReadBytes(); err != nil {
return err
}
if m.LuxAssetID, err = r.ReadBytes(); err != nil {
return err
}
if m.ChainDataDir, err = r.ReadString(); err != nil {
return err
}
if m.GenesisBytes, err = r.ReadBytes(); err != nil {
return err
}
if m.UpgradeBytes, err = r.ReadBytes(); err != nil {
return err
}
if m.ConfigBytes, err = r.ReadBytes(); err != nil {
return err
}
if m.DBServerAddr, err = r.ReadString(); err != nil {
return err
}
if m.ServerAddr, err = r.ReadString(); err != nil {
return err
}
return m.NetworkUpgrades.Decode(r)
}
// InitializeResponse contains initialization results
type InitializeResponse struct {
LastAcceptedID []byte
LastAcceptedParentID []byte
Height uint64
Bytes []byte
Timestamp int64
}
// Encode serializes InitializeResponse to the buffer
func (m *InitializeResponse) Encode(buf *Buffer) {
buf.WriteBytes(m.LastAcceptedID)
buf.WriteBytes(m.LastAcceptedParentID)
buf.WriteUint64(m.Height)
buf.WriteBytes(m.Bytes)
buf.WriteInt64(m.Timestamp)
}
// Decode deserializes InitializeResponse from the reader
func (m *InitializeResponse) Decode(r *Reader) error {
var err error
if m.LastAcceptedID, err = r.ReadBytes(); err != nil {
return err
}
if m.LastAcceptedParentID, err = r.ReadBytes(); err != nil {
return err
}
if m.Height, err = r.ReadUint64(); err != nil {
return err
}
if m.Bytes, err = r.ReadBytes(); err != nil {
return err
}
m.Timestamp, err = r.ReadInt64()
return err
}
// SetStateRequest contains state change request
type SetStateRequest struct {
State State
}
// Encode serializes SetStateRequest to the buffer
func (m *SetStateRequest) Encode(buf *Buffer) {
buf.WriteUint8(uint8(m.State))
}
// Decode deserializes SetStateRequest from the reader
func (m *SetStateRequest) Decode(r *Reader) error {
v, err := r.ReadUint8()
m.State = State(v)
return err
}
// SetStateResponse contains state change results
type SetStateResponse struct {
LastAcceptedID []byte
LastAcceptedParentID []byte
Height uint64
Bytes []byte
Timestamp int64
}
// Encode serializes SetStateResponse to the buffer
func (m *SetStateResponse) Encode(buf *Buffer) {
buf.WriteBytes(m.LastAcceptedID)
buf.WriteBytes(m.LastAcceptedParentID)
buf.WriteUint64(m.Height)
buf.WriteBytes(m.Bytes)
buf.WriteInt64(m.Timestamp)
}
// Decode deserializes SetStateResponse from the reader
func (m *SetStateResponse) Decode(r *Reader) error {
var err error
if m.LastAcceptedID, err = r.ReadBytes(); err != nil {
return err
}
if m.LastAcceptedParentID, err = r.ReadBytes(); err != nil {
return err
}
if m.Height, err = r.ReadUint64(); err != nil {
return err
}
if m.Bytes, err = r.ReadBytes(); err != nil {
return err
}
m.Timestamp, err = r.ReadInt64()
return err
}
// BuildBlockRequest contains block building parameters
type BuildBlockRequest struct {
PChainHeight uint64
HasPChainHeight bool
}
// Encode serializes BuildBlockRequest to the buffer
func (m *BuildBlockRequest) Encode(buf *Buffer) {
buf.WriteBool(m.HasPChainHeight)
if m.HasPChainHeight {
buf.WriteUint64(m.PChainHeight)
}
}
// Decode deserializes BuildBlockRequest from the reader
func (m *BuildBlockRequest) Decode(r *Reader) error {
var err error
if m.HasPChainHeight, err = r.ReadBool(); err != nil {
return err
}
if m.HasPChainHeight {
m.PChainHeight, err = r.ReadUint64()
}
return err
}
// BlockResponse contains block data (used by BuildBlock, ParseBlock, GetBlock)
type BlockResponse struct {
ID []byte
ParentID []byte
Bytes []byte // Zero-copy block data
Height uint64
Timestamp int64
VerifyWithContext bool
Err Error
}
// Encode serializes BlockResponse to the buffer
func (m *BlockResponse) Encode(buf *Buffer) {
buf.WriteBytes(m.ID)
buf.WriteBytes(m.ParentID)
buf.WriteBytes(m.Bytes)
buf.WriteUint64(m.Height)
buf.WriteInt64(m.Timestamp)
buf.WriteBool(m.VerifyWithContext)
buf.WriteUint8(uint8(m.Err))
}
// Decode deserializes BlockResponse from the reader
func (m *BlockResponse) Decode(r *Reader) error {
var err error
if m.ID, err = r.ReadBytes(); err != nil {
return err
}
if m.ParentID, err = r.ReadBytes(); err != nil {
return err
}
if m.Bytes, err = r.ReadBytes(); err != nil {
return err
}
if m.Height, err = r.ReadUint64(); err != nil {
return err
}
if m.Timestamp, err = r.ReadInt64(); err != nil {
return err
}
if m.VerifyWithContext, err = r.ReadBool(); err != nil {
return err
}
v, err := r.ReadUint8()
m.Err = Error(v)
return err
}
// ParseBlockRequest contains bytes to parse
type ParseBlockRequest struct {
Bytes []byte // Zero-copy input
}
// Encode serializes ParseBlockRequest to the buffer
func (m *ParseBlockRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.Bytes)
}
// Decode deserializes ParseBlockRequest from the reader
func (m *ParseBlockRequest) Decode(r *Reader) error {
var err error
m.Bytes, err = r.ReadBytes()
return err
}
// GetBlockRequest contains block ID to retrieve
type GetBlockRequest struct {
ID []byte
}
// Encode serializes GetBlockRequest to the buffer
func (m *GetBlockRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.ID)
}
// Decode deserializes GetBlockRequest from the reader
func (m *GetBlockRequest) Decode(r *Reader) error {
var err error
m.ID, err = r.ReadBytes()
return err
}
// SetPreferenceRequest contains preferred block ID
type SetPreferenceRequest struct {
ID []byte
}
// Encode serializes SetPreferenceRequest to the buffer
func (m *SetPreferenceRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.ID)
}
// Decode deserializes SetPreferenceRequest from the reader
func (m *SetPreferenceRequest) Decode(r *Reader) error {
var err error
m.ID, err = r.ReadBytes()
return err
}
// BlockVerifyRequest contains block verification parameters
type BlockVerifyRequest struct {
Bytes []byte
PChainHeight uint64
HasPChainHeight bool
}
// Encode serializes BlockVerifyRequest to the buffer
func (m *BlockVerifyRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.Bytes)
buf.WriteBool(m.HasPChainHeight)
if m.HasPChainHeight {
buf.WriteUint64(m.PChainHeight)
}
}
// Decode deserializes BlockVerifyRequest from the reader
func (m *BlockVerifyRequest) Decode(r *Reader) error {
var err error
if m.Bytes, err = r.ReadBytes(); err != nil {
return err
}
if m.HasPChainHeight, err = r.ReadBool(); err != nil {
return err
}
if m.HasPChainHeight {
m.PChainHeight, err = r.ReadUint64()
}
return err
}
// BlockVerifyResponse contains verification result
type BlockVerifyResponse struct {
Timestamp int64
}
// Encode serializes BlockVerifyResponse to the buffer
func (m *BlockVerifyResponse) Encode(buf *Buffer) {
buf.WriteInt64(m.Timestamp)
}
// Decode deserializes BlockVerifyResponse from the reader
func (m *BlockVerifyResponse) Decode(r *Reader) error {
var err error
m.Timestamp, err = r.ReadInt64()
return err
}
// BlockAcceptRequest contains block ID to accept
type BlockAcceptRequest struct {
ID []byte
}
// Encode serializes BlockAcceptRequest to the buffer
func (m *BlockAcceptRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.ID)
}
// Decode deserializes BlockAcceptRequest from the reader
func (m *BlockAcceptRequest) Decode(r *Reader) error {
var err error
m.ID, err = r.ReadBytes()
return err
}
// BlockRejectRequest contains block ID to reject
type BlockRejectRequest struct {
ID []byte
}
// Encode serializes BlockRejectRequest to the buffer
func (m *BlockRejectRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.ID)
}
// Decode deserializes BlockRejectRequest from the reader
func (m *BlockRejectRequest) Decode(r *Reader) error {
var err error
m.ID, err = r.ReadBytes()
return err
}
// BatchedParseBlockRequest contains multiple blocks to parse
type BatchedParseBlockRequest struct {
Requests [][]byte
}
// Encode serializes BatchedParseBlockRequest to the buffer
func (m *BatchedParseBlockRequest) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(m.Requests)))
for _, req := range m.Requests {
buf.WriteBytes(req)
}
}
// Decode deserializes BatchedParseBlockRequest from the reader
func (m *BatchedParseBlockRequest) Decode(r *Reader) error {
count, err := r.ReadUint32()
if err != nil {
return err
}
m.Requests = make([][]byte, count)
for i := uint32(0); i < count; i++ {
if m.Requests[i], err = r.ReadBytes(); err != nil {
return err
}
}
return nil
}
// BatchedParseBlockResponse contains parsed blocks
type BatchedParseBlockResponse struct {
Responses []BlockResponse
}
// Encode serializes BatchedParseBlockResponse to the buffer
func (m *BatchedParseBlockResponse) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(m.Responses)))
for i := range m.Responses {
m.Responses[i].Encode(buf)
}
}
// Decode deserializes BatchedParseBlockResponse from the reader
func (m *BatchedParseBlockResponse) Decode(r *Reader) error {
count, err := r.ReadUint32()
if err != nil {
return err
}
m.Responses = make([]BlockResponse, count)
for i := uint32(0); i < count; i++ {
if err := m.Responses[i].Decode(r); err != nil {
return err
}
}
return nil
}
// GetAncestorsRequest contains ancestor retrieval parameters
type GetAncestorsRequest struct {
BlkID []byte
MaxBlocksNum int32
MaxBlocksSize int32
MaxBlocksRetrivalTime int64
}
// Encode serializes GetAncestorsRequest to the buffer
func (m *GetAncestorsRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.BlkID)
buf.WriteInt32(m.MaxBlocksNum)
buf.WriteInt32(m.MaxBlocksSize)
buf.WriteInt64(m.MaxBlocksRetrivalTime)
}
// Decode deserializes GetAncestorsRequest from the reader
func (m *GetAncestorsRequest) Decode(r *Reader) error {
var err error
if m.BlkID, err = r.ReadBytes(); err != nil {
return err
}
if m.MaxBlocksNum, err = r.ReadInt32(); err != nil {
return err
}
if m.MaxBlocksSize, err = r.ReadInt32(); err != nil {
return err
}
m.MaxBlocksRetrivalTime, err = r.ReadInt64()
return err
}
// GetAncestorsResponse contains ancestor blocks
type GetAncestorsResponse struct {
BlksBytes [][]byte
}
// Encode serializes GetAncestorsResponse to the buffer
func (m *GetAncestorsResponse) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(m.BlksBytes)))
for _, blk := range m.BlksBytes {
buf.WriteBytes(blk)
}
}
// Decode deserializes GetAncestorsResponse from the reader
func (m *GetAncestorsResponse) Decode(r *Reader) error {
count, err := r.ReadUint32()
if err != nil {
return err
}
m.BlksBytes = make([][]byte, count)
for i := uint32(0); i < count; i++ {
if m.BlksBytes[i], err = r.ReadBytes(); err != nil {
return err
}
}
return nil
}
// GetBlockIDAtHeightRequest contains height to query
type GetBlockIDAtHeightRequest struct {
Height uint64
}
// Encode serializes GetBlockIDAtHeightRequest to the buffer
func (m *GetBlockIDAtHeightRequest) Encode(buf *Buffer) {
buf.WriteUint64(m.Height)
}
// Decode deserializes GetBlockIDAtHeightRequest from the reader
func (m *GetBlockIDAtHeightRequest) Decode(r *Reader) error {
var err error
m.Height, err = r.ReadUint64()
return err
}
// GetBlockIDAtHeightResponse contains block ID at height
type GetBlockIDAtHeightResponse struct {
BlkID []byte
Err Error
}
// Encode serializes GetBlockIDAtHeightResponse to the buffer
func (m *GetBlockIDAtHeightResponse) Encode(buf *Buffer) {
buf.WriteBytes(m.BlkID)
buf.WriteUint8(uint8(m.Err))
}
// Decode deserializes GetBlockIDAtHeightResponse from the reader
func (m *GetBlockIDAtHeightResponse) Decode(r *Reader) error {
var err error
if m.BlkID, err = r.ReadBytes(); err != nil {
return err
}
v, err := r.ReadUint8()
m.Err = Error(v)
return err
}
// RequestMsg contains incoming request data
type RequestMsg struct {
NodeID []byte
RequestID uint32
Deadline int64
Request []byte
}
// Encode serializes RequestMsg to the buffer
func (m *RequestMsg) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteUint32(m.RequestID)
buf.WriteInt64(m.Deadline)
buf.WriteBytes(m.Request)
}
// Decode deserializes RequestMsg from the reader
func (m *RequestMsg) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.RequestID, err = r.ReadUint32(); err != nil {
return err
}
if m.Deadline, err = r.ReadInt64(); err != nil {
return err
}
m.Request, err = r.ReadBytes()
return err
}
// RequestFailedMsg contains failed request info
type RequestFailedMsg struct {
NodeID []byte
RequestID uint32
ErrorCode int32
ErrorMessage string
}
// Encode serializes RequestFailedMsg to the buffer
func (m *RequestFailedMsg) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteUint32(m.RequestID)
buf.WriteInt32(m.ErrorCode)
buf.WriteString(m.ErrorMessage)
}
// Decode deserializes RequestFailedMsg from the reader
func (m *RequestFailedMsg) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.RequestID, err = r.ReadUint32(); err != nil {
return err
}
if m.ErrorCode, err = r.ReadInt32(); err != nil {
return err
}
m.ErrorMessage, err = r.ReadString()
return err
}
// ResponseMsg contains response data
type ResponseMsg struct {
NodeID []byte
RequestID uint32
Response []byte
}
// Encode serializes ResponseMsg to the buffer
func (m *ResponseMsg) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteUint32(m.RequestID)
buf.WriteBytes(m.Response)
}
// Decode deserializes ResponseMsg from the reader
func (m *ResponseMsg) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.RequestID, err = r.ReadUint32(); err != nil {
return err
}
m.Response, err = r.ReadBytes()
return err
}
// GossipMsg contains gossip message
type GossipMsg struct {
NodeID []byte
Msg []byte
}
// Encode serializes GossipMsg to the buffer
func (m *GossipMsg) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteBytes(m.Msg)
}
// Decode deserializes GossipMsg from the reader
func (m *GossipMsg) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
m.Msg, err = r.ReadBytes()
return err
}
// ConnectedRequest contains connection info
type ConnectedRequest struct {
NodeID []byte
Name string
Major uint32
Minor uint32
Patch uint32
}
// Encode serializes ConnectedRequest to the buffer
func (m *ConnectedRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
buf.WriteString(m.Name)
buf.WriteUint32(m.Major)
buf.WriteUint32(m.Minor)
buf.WriteUint32(m.Patch)
}
// Decode deserializes ConnectedRequest from the reader
func (m *ConnectedRequest) Decode(r *Reader) error {
var err error
if m.NodeID, err = r.ReadBytes(); err != nil {
return err
}
if m.Name, err = r.ReadString(); err != nil {
return err
}
if m.Major, err = r.ReadUint32(); err != nil {
return err
}
if m.Minor, err = r.ReadUint32(); err != nil {
return err
}
m.Patch, err = r.ReadUint32()
return err
}
// DisconnectedRequest contains disconnection info
type DisconnectedRequest struct {
NodeID []byte
}
// Encode serializes DisconnectedRequest to the buffer
func (m *DisconnectedRequest) Encode(buf *Buffer) {
buf.WriteBytes(m.NodeID)
}
// Decode deserializes DisconnectedRequest from the reader
func (m *DisconnectedRequest) Decode(r *Reader) error {
var err error
m.NodeID, err = r.ReadBytes()
return err
}
// HealthResponse contains health check result
type HealthResponse struct {
Details []byte
}
// Encode serializes HealthResponse to the buffer
func (m *HealthResponse) Encode(buf *Buffer) {
buf.WriteBytes(m.Details)
}
// Decode deserializes HealthResponse from the reader
func (m *HealthResponse) Decode(r *Reader) error {
var err error
m.Details, err = r.ReadBytes()
return err
}
// VersionResponse contains version info
type VersionResponse struct {
Version string
}
// Encode serializes VersionResponse to the buffer
func (m *VersionResponse) Encode(buf *Buffer) {
buf.WriteString(m.Version)
}
// Decode deserializes VersionResponse from the reader
func (m *VersionResponse) Decode(r *Reader) error {
var err error
m.Version, err = r.ReadString()
return err
}
// WaitForEventResponse contains event type
type WaitForEventResponse struct {
Message uint8
}
// Encode serializes WaitForEventResponse to the buffer
func (m *WaitForEventResponse) Encode(buf *Buffer) {
buf.WriteUint8(m.Message)
}
// Decode deserializes WaitForEventResponse from the reader
func (m *WaitForEventResponse) Decode(r *Reader) error {
var err error
m.Message, err = r.ReadUint8()
return err
}
// HTTPHandler contains HTTP handler info
type HTTPHandler struct {
Prefix string
ServerAddr string
}
// Encode serializes HTTPHandler to the buffer
func (m *HTTPHandler) Encode(buf *Buffer) {
buf.WriteString(m.Prefix)
buf.WriteString(m.ServerAddr)
}
// Decode deserializes HTTPHandler from the reader
func (m *HTTPHandler) Decode(r *Reader) error {
var err error
if m.Prefix, err = r.ReadString(); err != nil {
return err
}
m.ServerAddr, err = r.ReadString()
return err
}
// CreateHandlersResponse contains handler list
type CreateHandlersResponse struct {
Handlers []HTTPHandler
}
// Encode serializes CreateHandlersResponse to the buffer
func (m *CreateHandlersResponse) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(m.Handlers)))
for i := range m.Handlers {
m.Handlers[i].Encode(buf)
}
}
// Decode deserializes CreateHandlersResponse from the reader
func (m *CreateHandlersResponse) Decode(r *Reader) error {
count, err := r.ReadUint32()
if err != nil {
return err
}
m.Handlers = make([]HTTPHandler, count)
for i := uint32(0); i < count; i++ {
if err := m.Handlers[i].Decode(r); err != nil {
return err
}
}
return nil
}
// NewHTTPHandlerResponse contains HTTP handler address
type NewHTTPHandlerResponse struct {
ServerAddr string
}
// Encode serializes NewHTTPHandlerResponse to the buffer
func (m *NewHTTPHandlerResponse) Encode(buf *Buffer) {
buf.WriteString(m.ServerAddr)
}
// Decode deserializes NewHTTPHandlerResponse from the reader
func (m *NewHTTPHandlerResponse) Decode(r *Reader) error {
var err error
m.ServerAddr, err = r.ReadString()
return err
}
+165
View File
@@ -0,0 +1,165 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
// WarpSignRequest encodes a warp signing request
type WarpSignRequest struct {
NetworkID uint32
SourceChainID []byte // 32 bytes
Payload []byte
}
// Encode writes the request to the buffer
func (r *WarpSignRequest) Encode(buf *Buffer) {
buf.WriteUint32(r.NetworkID)
buf.WriteBytes(r.SourceChainID)
buf.WriteBytes(r.Payload)
}
// Decode reads the request from the reader
func (r *WarpSignRequest) Decode(rd *Reader) error {
var err error
r.NetworkID, err = rd.ReadUint32()
if err != nil {
return err
}
r.SourceChainID, err = rd.ReadBytes()
if err != nil {
return err
}
r.Payload, err = rd.ReadBytes()
return err
}
// WarpSignResponse encodes a warp signing response
type WarpSignResponse struct {
Signature []byte
Error string
}
// Encode writes the response to the buffer
func (r *WarpSignResponse) Encode(buf *Buffer) {
buf.WriteBytes(r.Signature)
buf.WriteString(r.Error)
}
// Decode reads the response from the reader
func (r *WarpSignResponse) Decode(rd *Reader) error {
var err error
r.Signature, err = rd.ReadBytes()
if err != nil {
return err
}
r.Error, err = rd.ReadString()
return err
}
// WarpGetPublicKeyRequest encodes a get public key request
type WarpGetPublicKeyRequest struct{}
// Encode writes the request to the buffer
func (r *WarpGetPublicKeyRequest) Encode(buf *Buffer) {
// No fields
}
// Decode reads the request from the reader
func (r *WarpGetPublicKeyRequest) Decode(rd *Reader) error {
return nil
}
// WarpGetPublicKeyResponse encodes a get public key response
type WarpGetPublicKeyResponse struct {
PublicKey []byte
Error string
}
// Encode writes the response to the buffer
func (r *WarpGetPublicKeyResponse) Encode(buf *Buffer) {
buf.WriteBytes(r.PublicKey)
buf.WriteString(r.Error)
}
// Decode reads the response from the reader
func (r *WarpGetPublicKeyResponse) Decode(rd *Reader) error {
var err error
r.PublicKey, err = rd.ReadBytes()
if err != nil {
return err
}
r.Error, err = rd.ReadString()
return err
}
// WarpBatchSignRequest encodes a batch signing request
type WarpBatchSignRequest struct {
Messages []WarpSignRequest
}
// Encode writes the request to the buffer
func (r *WarpBatchSignRequest) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(r.Messages)))
for i := range r.Messages {
r.Messages[i].Encode(buf)
}
}
// Decode reads the request from the reader
func (r *WarpBatchSignRequest) Decode(rd *Reader) error {
count, err := rd.ReadUint32()
if err != nil {
return err
}
r.Messages = make([]WarpSignRequest, count)
for i := uint32(0); i < count; i++ {
if err := r.Messages[i].Decode(rd); err != nil {
return err
}
}
return nil
}
// WarpBatchSignResponse encodes a batch signing response
type WarpBatchSignResponse struct {
Signatures [][]byte
Errors []string
}
// Encode writes the response to the buffer
func (r *WarpBatchSignResponse) Encode(buf *Buffer) {
buf.WriteUint32(uint32(len(r.Signatures)))
for _, sig := range r.Signatures {
buf.WriteBytes(sig)
}
buf.WriteUint32(uint32(len(r.Errors)))
for _, errStr := range r.Errors {
buf.WriteString(errStr)
}
}
// Decode reads the response from the reader
func (r *WarpBatchSignResponse) Decode(rd *Reader) error {
sigCount, err := rd.ReadUint32()
if err != nil {
return err
}
r.Signatures = make([][]byte, sigCount)
for i := uint32(0); i < sigCount; i++ {
r.Signatures[i], err = rd.ReadBytes()
if err != nil {
return err
}
}
errCount, err := rd.ReadUint32()
if err != nil {
return err
}
r.Errors = make([]string, errCount)
for i := uint32(0); i < errCount; i++ {
r.Errors[i], err = rd.ReadString()
if err != nil {
return err
}
}
return nil
}
+337
View File
@@ -0,0 +1,337 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
import (
"encoding/binary"
"errors"
"io"
"sync"
)
const (
// MaxMessageSize is the maximum allowed message size (16MB)
MaxMessageSize = 16 * 1024 * 1024
// HeaderSize is the size of the message header (4 bytes length + 1 byte type)
HeaderSize = 5
// DefaultBufferSize for pooled buffers
DefaultBufferSize = 64 * 1024
)
var (
ErrMessageTooLarge = errors.New("zap: message exceeds maximum size")
ErrInvalidMessage = errors.New("zap: invalid message format")
ErrUnknownType = errors.New("zap: unknown message type")
)
// MessageType identifies the RPC method being called
type MessageType uint8
const (
// VM service methods (1-31)
MsgInitialize MessageType = iota + 1
MsgSetState
MsgShutdown
MsgCreateHandlers
MsgNewHTTPHandler
MsgWaitForEvent
MsgConnected
MsgDisconnected
MsgBuildBlock
MsgParseBlock
MsgGetBlock
MsgSetPreference
MsgHealth
MsgVersion
MsgRequest
MsgRequestFailed
MsgResponse
MsgGossip
MsgGather
MsgGetAncestors
MsgBatchedParseBlock
MsgGetBlockIDAtHeight
MsgStateSyncEnabled
MsgGetOngoingSyncStateSummary
MsgGetLastStateSummary
MsgParseStateSummary
MsgGetStateSummary
MsgBlockVerify
MsgBlockAccept
MsgBlockReject
MsgStateSummaryAccept // = 31
// p2p.Sender methods (40-49)
MsgSendRequest MessageType = 40
MsgSendResponse MessageType = 41
MsgSendError MessageType = 42
MsgSendGossip MessageType = 43
// Warp signing methods (50-59)
MsgWarpSign MessageType = 50
MsgWarpGetPublicKey MessageType = 51
MsgWarpBatchSign MessageType = 52
// Response flag - set on response messages (high bit)
// All message types must be < 128 to allow OR with this flag
MsgResponseFlag MessageType = 128
)
// Buffer is a reusable byte buffer for zero-copy operations
type Buffer struct {
Data []byte
offset int
}
// BufferPool manages reusable buffers to minimize allocations
var BufferPool = sync.Pool{
New: func() interface{} {
return &Buffer{
Data: make([]byte, DefaultBufferSize),
}
},
}
// GetBuffer retrieves a buffer from the pool
func GetBuffer() *Buffer {
buf := BufferPool.Get().(*Buffer)
buf.Reset()
return buf
}
// PutBuffer returns a buffer to the pool
func PutBuffer(buf *Buffer) {
if buf != nil && cap(buf.Data) <= MaxMessageSize {
BufferPool.Put(buf)
}
}
// Reset prepares the buffer for reuse
func (b *Buffer) Reset() {
b.offset = 0
b.Data = b.Data[:cap(b.Data)]
}
// Grow ensures the buffer has at least n bytes available
func (b *Buffer) Grow(n int) {
if cap(b.Data) < n {
newData := make([]byte, n*2)
copy(newData, b.Data[:b.offset])
b.Data = newData
}
if len(b.Data) < n {
b.Data = b.Data[:cap(b.Data)]
}
}
// WriteUint8 writes a uint8 to the buffer
func (b *Buffer) WriteUint8(v uint8) {
b.Grow(b.offset + 1)
b.Data[b.offset] = v
b.offset++
}
// WriteUint16 writes a uint16 to the buffer (big-endian)
func (b *Buffer) WriteUint16(v uint16) {
b.Grow(b.offset + 2)
binary.BigEndian.PutUint16(b.Data[b.offset:], v)
b.offset += 2
}
// WriteUint32 writes a uint32 to the buffer (big-endian)
func (b *Buffer) WriteUint32(v uint32) {
b.Grow(b.offset + 4)
binary.BigEndian.PutUint32(b.Data[b.offset:], v)
b.offset += 4
}
// WriteUint64 writes a uint64 to the buffer (big-endian)
func (b *Buffer) WriteUint64(v uint64) {
b.Grow(b.offset + 8)
binary.BigEndian.PutUint64(b.Data[b.offset:], v)
b.offset += 8
}
// WriteInt32 writes an int32 to the buffer (big-endian)
func (b *Buffer) WriteInt32(v int32) {
b.WriteUint32(uint32(v))
}
// WriteInt64 writes an int64 to the buffer (big-endian)
func (b *Buffer) WriteInt64(v int64) {
b.WriteUint64(uint64(v))
}
// WriteBool writes a boolean to the buffer
func (b *Buffer) WriteBool(v bool) {
if v {
b.WriteUint8(1)
} else {
b.WriteUint8(0)
}
}
// WriteBytes writes a length-prefixed byte slice to the buffer
func (b *Buffer) WriteBytes(data []byte) {
b.WriteUint32(uint32(len(data)))
b.Grow(b.offset + len(data))
copy(b.Data[b.offset:], data)
b.offset += len(data)
}
// WriteString writes a length-prefixed string to the buffer
func (b *Buffer) WriteString(s string) {
b.WriteBytes([]byte(s))
}
// Bytes returns the written portion of the buffer
func (b *Buffer) Bytes() []byte {
return b.Data[:b.offset]
}
// Len returns the number of bytes written
func (b *Buffer) Len() int {
return b.offset
}
// Reader provides zero-copy reading from a byte slice
type Reader struct {
data []byte
offset int
}
// NewReader creates a new reader from a byte slice
func NewReader(data []byte) *Reader {
return &Reader{data: data}
}
// ReadUint8 reads a uint8 from the buffer
func (r *Reader) ReadUint8() (uint8, error) {
if r.offset+1 > len(r.data) {
return 0, io.ErrUnexpectedEOF
}
v := r.data[r.offset]
r.offset++
return v, nil
}
// ReadUint16 reads a uint16 from the buffer (big-endian)
func (r *Reader) ReadUint16() (uint16, error) {
if r.offset+2 > len(r.data) {
return 0, io.ErrUnexpectedEOF
}
v := binary.BigEndian.Uint16(r.data[r.offset:])
r.offset += 2
return v, nil
}
// ReadUint32 reads a uint32 from the buffer (big-endian)
func (r *Reader) ReadUint32() (uint32, error) {
if r.offset+4 > len(r.data) {
return 0, io.ErrUnexpectedEOF
}
v := binary.BigEndian.Uint32(r.data[r.offset:])
r.offset += 4
return v, nil
}
// ReadUint64 reads a uint64 from the buffer (big-endian)
func (r *Reader) ReadUint64() (uint64, error) {
if r.offset+8 > len(r.data) {
return 0, io.ErrUnexpectedEOF
}
v := binary.BigEndian.Uint64(r.data[r.offset:])
r.offset += 8
return v, nil
}
// ReadInt32 reads an int32 from the buffer (big-endian)
func (r *Reader) ReadInt32() (int32, error) {
v, err := r.ReadUint32()
return int32(v), err
}
// ReadInt64 reads an int64 from the buffer (big-endian)
func (r *Reader) ReadInt64() (int64, error) {
v, err := r.ReadUint64()
return int64(v), err
}
// ReadBool reads a boolean from the buffer
func (r *Reader) ReadBool() (bool, error) {
v, err := r.ReadUint8()
return v != 0, err
}
// ReadBytes reads a length-prefixed byte slice (zero-copy - returns slice into original buffer)
func (r *Reader) ReadBytes() ([]byte, error) {
length, err := r.ReadUint32()
if err != nil {
return nil, err
}
if r.offset+int(length) > len(r.data) {
return nil, io.ErrUnexpectedEOF
}
// Zero-copy: return slice into original buffer
data := r.data[r.offset : r.offset+int(length)]
r.offset += int(length)
return data, nil
}
// ReadString reads a length-prefixed string
func (r *Reader) ReadString() (string, error) {
data, err := r.ReadBytes()
if err != nil {
return "", err
}
return string(data), nil
}
// Remaining returns the number of unread bytes
func (r *Reader) Remaining() int {
return len(r.data) - r.offset
}
// WriteMessage writes a complete ZAP message with header
func WriteMessage(w io.Writer, msgType MessageType, payload []byte) error {
if len(payload) > MaxMessageSize {
return ErrMessageTooLarge
}
header := make([]byte, HeaderSize)
binary.BigEndian.PutUint32(header[0:4], uint32(len(payload)))
header[4] = byte(msgType)
if _, err := w.Write(header); err != nil {
return err
}
if _, err := w.Write(payload); err != nil {
return err
}
return nil
}
// ReadMessage reads a complete ZAP message with header
func ReadMessage(r io.Reader) (MessageType, []byte, error) {
header := make([]byte, HeaderSize)
if _, err := io.ReadFull(r, header); err != nil {
return 0, nil, err
}
length := binary.BigEndian.Uint32(header[0:4])
msgType := MessageType(header[4])
if length > MaxMessageSize {
return 0, nil, ErrMessageTooLarge
}
payload := make([]byte, length)
if _, err := io.ReadFull(r, payload); err != nil {
return 0, nil, err
}
return msgType, payload, nil
}
+306
View File
@@ -0,0 +1,306 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zap
import (
"bytes"
"testing"
)
func TestBuffer(t *testing.T) {
buf := GetBuffer()
defer PutBuffer(buf)
// Write various types
buf.WriteUint8(0x42)
buf.WriteUint16(0x1234)
buf.WriteUint32(0xDEADBEEF)
buf.WriteUint64(0xCAFEBABEDEADBEEF)
buf.WriteInt32(-12345)
buf.WriteInt64(-9876543210)
buf.WriteBool(true)
buf.WriteBool(false)
buf.WriteBytes([]byte("hello"))
buf.WriteString("world")
// Read back
r := NewReader(buf.Bytes())
v8, err := r.ReadUint8()
if err != nil || v8 != 0x42 {
t.Errorf("ReadUint8: got %v, %v", v8, err)
}
v16, err := r.ReadUint16()
if err != nil || v16 != 0x1234 {
t.Errorf("ReadUint16: got %v, %v", v16, err)
}
v32, err := r.ReadUint32()
if err != nil || v32 != 0xDEADBEEF {
t.Errorf("ReadUint32: got %v, %v", v32, err)
}
v64, err := r.ReadUint64()
if err != nil || v64 != 0xCAFEBABEDEADBEEF {
t.Errorf("ReadUint64: got %v, %v", v64, err)
}
i32, err := r.ReadInt32()
if err != nil || i32 != -12345 {
t.Errorf("ReadInt32: got %v, %v", i32, err)
}
i64, err := r.ReadInt64()
if err != nil || i64 != -9876543210 {
t.Errorf("ReadInt64: got %v, %v", i64, err)
}
b1, err := r.ReadBool()
if err != nil || !b1 {
t.Errorf("ReadBool: got %v, %v", b1, err)
}
b2, err := r.ReadBool()
if err != nil || b2 {
t.Errorf("ReadBool: got %v, %v", b2, err)
}
data, err := r.ReadBytes()
if err != nil || string(data) != "hello" {
t.Errorf("ReadBytes: got %v, %v", string(data), err)
}
str, err := r.ReadString()
if err != nil || str != "world" {
t.Errorf("ReadString: got %v, %v", str, err)
}
if r.Remaining() != 0 {
t.Errorf("Expected 0 remaining, got %d", r.Remaining())
}
}
func TestMessage(t *testing.T) {
var buf bytes.Buffer
// Write message
payload := []byte("test payload")
if err := WriteMessage(&buf, MsgBuildBlock, payload); err != nil {
t.Fatalf("WriteMessage: %v", err)
}
// Read message
msgType, data, err := ReadMessage(&buf)
if err != nil {
t.Fatalf("ReadMessage: %v", err)
}
if msgType != MsgBuildBlock {
t.Errorf("Expected MsgBuildBlock, got %d", msgType)
}
if !bytes.Equal(data, payload) {
t.Errorf("Payload mismatch: got %v, want %v", data, payload)
}
}
func TestBlockResponse(t *testing.T) {
orig := &BlockResponse{
ID: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
ParentID: []byte{32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
Bytes: make([]byte, 1024),
Height: 12345,
Timestamp: 1704067200,
VerifyWithContext: true,
Err: ErrorUnspecified,
}
// Fill block bytes with test data
for i := range orig.Bytes {
orig.Bytes[i] = byte(i % 256)
}
buf := GetBuffer()
defer PutBuffer(buf)
orig.Encode(buf)
decoded := &BlockResponse{}
if err := decoded.Decode(NewReader(buf.Bytes())); err != nil {
t.Fatalf("Decode: %v", err)
}
if !bytes.Equal(decoded.ID, orig.ID) {
t.Error("ID mismatch")
}
if !bytes.Equal(decoded.ParentID, orig.ParentID) {
t.Error("ParentID mismatch")
}
if !bytes.Equal(decoded.Bytes, orig.Bytes) {
t.Error("Bytes mismatch")
}
if decoded.Height != orig.Height {
t.Errorf("Height: got %d, want %d", decoded.Height, orig.Height)
}
if decoded.Timestamp != orig.Timestamp {
t.Errorf("Timestamp: got %d, want %d", decoded.Timestamp, orig.Timestamp)
}
if decoded.VerifyWithContext != orig.VerifyWithContext {
t.Error("VerifyWithContext mismatch")
}
if decoded.Err != orig.Err {
t.Error("Err mismatch")
}
}
func TestBatchedParseBlockRequest(t *testing.T) {
orig := &BatchedParseBlockRequest{
Requests: [][]byte{
make([]byte, 100),
make([]byte, 200),
make([]byte, 300),
},
}
for i, req := range orig.Requests {
for j := range req {
req[j] = byte((i + j) % 256)
}
}
buf := GetBuffer()
defer PutBuffer(buf)
orig.Encode(buf)
decoded := &BatchedParseBlockRequest{}
if err := decoded.Decode(NewReader(buf.Bytes())); err != nil {
t.Fatalf("Decode: %v", err)
}
if len(decoded.Requests) != len(orig.Requests) {
t.Fatalf("Request count: got %d, want %d", len(decoded.Requests), len(orig.Requests))
}
for i := range orig.Requests {
if !bytes.Equal(decoded.Requests[i], orig.Requests[i]) {
t.Errorf("Request %d mismatch", i)
}
}
}
func TestSendGossipMsg(t *testing.T) {
orig := &SendGossipMsg{
NodeIDs: [][]byte{
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
{21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
},
Validators: 10,
NonValidators: 5,
Peers: 100,
Msg: []byte("gossip message payload"),
}
buf := GetBuffer()
defer PutBuffer(buf)
orig.Encode(buf)
decoded := &SendGossipMsg{}
if err := decoded.Decode(NewReader(buf.Bytes())); err != nil {
t.Fatalf("Decode: %v", err)
}
if len(decoded.NodeIDs) != len(orig.NodeIDs) {
t.Fatalf("NodeID count: got %d, want %d", len(decoded.NodeIDs), len(orig.NodeIDs))
}
for i := range orig.NodeIDs {
if !bytes.Equal(decoded.NodeIDs[i], orig.NodeIDs[i]) {
t.Errorf("NodeID %d mismatch", i)
}
}
if decoded.Validators != orig.Validators {
t.Errorf("Validators: got %d, want %d", decoded.Validators, orig.Validators)
}
if decoded.NonValidators != orig.NonValidators {
t.Errorf("NonValidators: got %d, want %d", decoded.NonValidators, orig.NonValidators)
}
if decoded.Peers != orig.Peers {
t.Errorf("Peers: got %d, want %d", decoded.Peers, orig.Peers)
}
if !bytes.Equal(decoded.Msg, orig.Msg) {
t.Error("Msg mismatch")
}
}
func BenchmarkBufferWrite(b *testing.B) {
buf := GetBuffer()
defer PutBuffer(buf)
data := make([]byte, 1024)
b.ResetTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
buf.WriteUint64(12345678901234567890)
buf.WriteBytes(data)
buf.WriteString("test string")
}
}
func BenchmarkBufferRead(b *testing.B) {
buf := GetBuffer()
buf.WriteUint64(12345678901234567890)
buf.WriteBytes(make([]byte, 1024))
buf.WriteString("test string")
data := buf.Bytes()
PutBuffer(buf)
b.ResetTimer()
for i := 0; i < b.N; i++ {
r := NewReader(data)
_, _ = r.ReadUint64()
_, _ = r.ReadBytes()
_, _ = r.ReadString()
}
}
func BenchmarkBlockResponse(b *testing.B) {
resp := &BlockResponse{
ID: make([]byte, 32),
ParentID: make([]byte, 32),
Bytes: make([]byte, 100*1024), // 100KB block
Height: 12345,
Timestamp: 1704067200,
VerifyWithContext: true,
Err: ErrorUnspecified,
}
buf := GetBuffer()
defer PutBuffer(buf)
b.Run("Encode", func(b *testing.B) {
for i := 0; i < b.N; i++ {
buf.Reset()
resp.Encode(buf)
}
b.SetBytes(int64(buf.Len()))
})
resp.Encode(buf)
data := make([]byte, len(buf.Bytes()))
copy(data, buf.Bytes())
b.Run("Decode", func(b *testing.B) {
decoded := &BlockResponse{}
for i := 0; i < b.N; i++ {
r := NewReader(data)
_ = decoded.Decode(r)
}
b.SetBytes(int64(len(data)))
})
}