mirror of
https://github.com/luxfi/api.git
synced 2026-09-24 00:24:53 +00:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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)))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user