mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
The module already called itself github.com/luxfi/node while containing no node in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and the Go column of the matrix measured a VM plugin. A repo that fetches the thing it claims to be has not replaced it, so the Go sources move in — 1515 files, the daemon and everything under it — and the build reaches outside this module for nothing. Rust and C++ sit beside it unchanged and are still measured against it by the differential. That is the point of one repo: the reference implementation and the two ports are the same checkout, so parity is a thing you can run rather than a thing you assert. Verified: builds, and the closure carries no lux-private — checked at build time rather than believed. The daemon starts twelve chains on a local network (P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
190 lines
5.8 KiB
Go
190 lines
5.8 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package block
|
|
|
|
// Native ZAP block wire: the struct IS the wire. There is no codec, no version
|
|
// prefix, no slot map. A 1-byte blockKind at object offset 0 is the whole
|
|
// dispatch — Parse reads it and returns the typed block (signed body vs option).
|
|
//
|
|
// A signed block is unsigned_buffer ‖ sig_buffer (both self-delimiting zap
|
|
// messages), so the unsigned bytes are a genuine byte-prefix of the signed
|
|
// bytes and the block ID = hash(unsigned prefix). An unsigned block (no
|
|
// certificate, no signature) is just the unsigned buffer with no suffix. An
|
|
// option is a single self-delimiting message.
|
|
//
|
|
// Unsigned-block object (kind = blkSigned), all offsets object-relative, LE:
|
|
//
|
|
// kind u8 @ 0
|
|
// ParentID 32B @ 1
|
|
// Timestamp i64 @ 33
|
|
// PChainHeight u64 @ 41
|
|
// Epoch.PChainHt u64 @ 49
|
|
// Epoch.Number u64 @ 57
|
|
// Epoch.StartTime i64 @ 65
|
|
// Certificate 8B @ 73 bytes ptr (opaque DER; part of the ID hash)
|
|
// Block 8B @ 81 bytes ptr (inner block bytes)
|
|
// DARoot 32B @ 89
|
|
// WitnessRoot 32B @ 121
|
|
// MessagesOutRoot 32B @ 153
|
|
// BlobCount u32 @ 185
|
|
// size 189
|
|
//
|
|
// Sig buffer object: Signature bytes ptr @ 0 size 8
|
|
// Header object: Chain 32B @0, Parent 32B @32, Body 32B @64 size 96
|
|
// Option object (kind = blkOption): kind u8 @0, ParentID 32B @1,
|
|
// InnerBytes bytes ptr @33 size 41
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/zap"
|
|
)
|
|
|
|
// blockKind is the 1-byte discriminator at object offset 0 of every parseable
|
|
// proposervm block buffer. Parse reads it and returns the typed block.
|
|
type blockKind uint8
|
|
|
|
const (
|
|
blkReserved blockKind = iota
|
|
blkSigned // statelessBlock (may carry cert + appended signature)
|
|
blkOption // option block
|
|
)
|
|
|
|
var errUnknownBlockType = errors.New("proposervm block: unknown block kind")
|
|
|
|
// Unsigned-block object offsets + size.
|
|
const (
|
|
offKind = 0
|
|
offParentID = 1
|
|
offTimestamp = 33
|
|
offPChainHt = 41
|
|
|
|
offEpochHt = 49
|
|
offEpochNum = 57
|
|
offEpochStart = 65
|
|
|
|
offCert = 73
|
|
offBlock = 81
|
|
|
|
offDARoot = 89
|
|
offWitnessRoot = 121
|
|
offMsgOutRoot = 153
|
|
offBlobCount = 185
|
|
|
|
sizeUnsigned = 189
|
|
|
|
idLen = 32
|
|
)
|
|
|
|
// Sig buffer object.
|
|
const (
|
|
offSig = 0
|
|
sizeSig = 8
|
|
)
|
|
|
|
// Header object.
|
|
const (
|
|
hdrChain = 0
|
|
hdrParent = 32
|
|
hdrBody = 64
|
|
sizeHeader = 96
|
|
)
|
|
|
|
// Option object.
|
|
const (
|
|
offOptParent = 1
|
|
offOptInner = 33
|
|
sizeOption = 41
|
|
)
|
|
|
|
// zapLen returns the total length of the leading self-delimiting zap message in
|
|
// b (its header size field @[12:16]). This is the split point between the
|
|
// unsigned prefix and the signature suffix of a signed block.
|
|
func zapLen(b []byte) (int, error) {
|
|
if len(b) < zap.HeaderSize {
|
|
return 0, zap.ErrBufferTooSmall
|
|
}
|
|
n := int(binary.LittleEndian.Uint32(b[12:16]))
|
|
if n < zap.HeaderSize || n > len(b) {
|
|
return 0, zap.ErrBufferTooSmall
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// buildUnsignedBuffer writes the unsigned block body as one zap object. cert is
|
|
// the opaque DER certificate (nil for an unsigned block). The v1.1 DA fields
|
|
// (DARoot/WitnessRoot/MessagesOutRoot/BlobCount) are written as zeros — no
|
|
// current constructor populates them, but they stay in the wire so the
|
|
// SignedBlock accessor surface round-trips if a future builder sets them.
|
|
func buildUnsignedBuffer(parentID ids.ID, timestamp int64, pChainHeight uint64, epoch Epoch, cert, blockBytes []byte) []byte {
|
|
var zero [idLen]byte
|
|
b := zap.NewBuilder(zap.HeaderSize + sizeUnsigned + len(cert) + len(blockBytes) + 64)
|
|
ob := b.StartObject(sizeUnsigned)
|
|
ob.SetUint8(offKind, uint8(blkSigned))
|
|
ob.SetBytesFixed(offParentID, parentID[:])
|
|
ob.SetInt64(offTimestamp, timestamp)
|
|
ob.SetUint64(offPChainHt, pChainHeight)
|
|
ob.SetUint64(offEpochHt, epoch.PChainHeight)
|
|
ob.SetUint64(offEpochNum, epoch.Number)
|
|
ob.SetInt64(offEpochStart, epoch.StartTime)
|
|
ob.SetBytes(offCert, cert)
|
|
ob.SetBytes(offBlock, blockBytes)
|
|
ob.SetBytesFixed(offDARoot, zero[:])
|
|
ob.SetBytesFixed(offWitnessRoot, zero[:])
|
|
ob.SetBytesFixed(offMsgOutRoot, zero[:])
|
|
ob.SetUint32(offBlobCount, 0)
|
|
ob.FinishAsRoot()
|
|
return b.Finish()
|
|
}
|
|
|
|
// buildSigBuffer wraps a proposer signature as a standalone zap message,
|
|
// appended after the unsigned prefix of a signed block.
|
|
func buildSigBuffer(sig []byte) []byte {
|
|
b := zap.NewBuilder(zap.HeaderSize + sizeSig + len(sig))
|
|
ob := b.StartObject(sizeSig)
|
|
ob.SetBytes(offSig, sig)
|
|
ob.FinishAsRoot()
|
|
return b.Finish()
|
|
}
|
|
|
|
// buildHeaderBuffer encodes the (chain, parent, body) header signed by the
|
|
// proposer. Deterministic fixed-shape object — Build and verify both hash it.
|
|
func buildHeaderBuffer(chainID, parentID, bodyID ids.ID) []byte {
|
|
b := zap.NewBuilder(zap.HeaderSize + sizeHeader)
|
|
ob := b.StartObject(sizeHeader)
|
|
ob.SetBytesFixed(hdrChain, chainID[:])
|
|
ob.SetBytesFixed(hdrParent, parentID[:])
|
|
ob.SetBytesFixed(hdrBody, bodyID[:])
|
|
ob.FinishAsRoot()
|
|
return b.Finish()
|
|
}
|
|
|
|
// buildOptionBuffer encodes an option block as one zap object.
|
|
func buildOptionBuffer(parentID ids.ID, innerBytes []byte) []byte {
|
|
b := zap.NewBuilder(zap.HeaderSize + sizeOption + len(innerBytes) + 32)
|
|
ob := b.StartObject(sizeOption)
|
|
ob.SetUint8(offKind, uint8(blkOption))
|
|
ob.SetBytesFixed(offOptParent, parentID[:])
|
|
ob.SetBytes(offOptInner, innerBytes)
|
|
ob.FinishAsRoot()
|
|
return b.Finish()
|
|
}
|
|
|
|
// read32 reads a 32-byte fixed field at the given object offset.
|
|
func read32(o zap.Object, off int) [idLen]byte {
|
|
var r [idLen]byte
|
|
copy(r[:], o.BytesFixedSlice(off, idLen))
|
|
return r
|
|
}
|
|
|
|
// concat returns a ‖ b as a fresh slice.
|
|
func concat(a, b []byte) []byte {
|
|
out := make([]byte, 0, len(a)+len(b))
|
|
out = append(out, a...)
|
|
out = append(out, b...)
|
|
return out
|
|
}
|