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.
136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package block
|
|
|
|
// Native ZAP wire for xsvm blocks: the struct IS the wire. One zap object per
|
|
// block; the txs are self-describing via their own tx.Marshal(), so a block
|
|
// stores the per-tx byte lengths as a u32 list and the concatenated tx bytes as
|
|
// a blob, then Parse re-splits and hands each slice to tx.Parse. There is no
|
|
// codec, no version prefix, no slot map.
|
|
//
|
|
// Object fixed section (offsets object-relative, little-endian):
|
|
//
|
|
// ParentID 32B @ 0
|
|
// Timestamp i64 @ 32
|
|
// Height u64 @ 40
|
|
// TxLengths 8B @ 48 u32 list ptr — one entry per tx
|
|
// TxBlob 8B @ 56 bytes ptr — concat of each tx.Marshal()
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/luxfi/crypto/hash"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/vms/example/xsvm/tx"
|
|
"github.com/luxfi/zap"
|
|
)
|
|
|
|
const (
|
|
offBlkParentID = 0
|
|
offBlkTimestamp = 32
|
|
offBlkHeight = 40
|
|
offBlkTxLengths = 48
|
|
offBlkTxBlob = 56
|
|
sizeBlk = 64
|
|
|
|
blkTxLenStride = 4 // uint32
|
|
blkIDLen = 32
|
|
)
|
|
|
|
// Stateless blocks are blocks as they are marshalled/unmarshalled and sent over
|
|
// the p2p network. The stateful blocks which can be executed are built from
|
|
// Stateless blocks.
|
|
type Stateless struct {
|
|
ParentID ids.ID `json:"parentID"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
Height uint64 `json:"height"`
|
|
Txs []*tx.Tx `json:"txs"`
|
|
}
|
|
|
|
func (b *Stateless) Time() time.Time {
|
|
return time.Unix(b.Timestamp, 0)
|
|
}
|
|
|
|
// Marshal encodes the block as one native ZAP object.
|
|
func (b *Stateless) Marshal() ([]byte, error) {
|
|
builder := zap.NewBuilder(zap.HeaderSize + sizeBlk + 256)
|
|
|
|
var (
|
|
txBlob []byte
|
|
lenOff int
|
|
lenCount int
|
|
)
|
|
if len(b.Txs) > 0 {
|
|
lb := builder.StartList(blkTxLenStride)
|
|
for i, t := range b.Txs {
|
|
raw, err := t.Marshal()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("xsvm/block: marshal tx %d: %w", i, err)
|
|
}
|
|
lb.AddUint32(uint32(len(raw)))
|
|
txBlob = append(txBlob, raw...)
|
|
}
|
|
lenOff, lenCount = lb.Finish()
|
|
}
|
|
|
|
ob := builder.StartObject(sizeBlk)
|
|
ob.SetBytesFixed(offBlkParentID, b.ParentID[:])
|
|
ob.SetInt64(offBlkTimestamp, b.Timestamp)
|
|
ob.SetUint64(offBlkHeight, b.Height)
|
|
ob.SetList(offBlkTxLengths, lenOff, lenCount)
|
|
ob.SetBytes(offBlkTxBlob, txBlob)
|
|
ob.FinishAsRoot()
|
|
return builder.Finish(), nil
|
|
}
|
|
|
|
func (b *Stateless) ID() (ids.ID, error) {
|
|
bytes, err := b.Marshal()
|
|
return hash.ComputeHash256Array(bytes), err
|
|
}
|
|
|
|
func Parse(bytes []byte) (*Stateless, error) {
|
|
msg, err := zap.Parse(bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
obj := msg.Root()
|
|
blk := &Stateless{
|
|
Timestamp: obj.Int64(offBlkTimestamp),
|
|
Height: obj.Uint64(offBlkHeight),
|
|
}
|
|
copy(blk.ParentID[:], obj.BytesFixedSlice(offBlkParentID, blkIDLen))
|
|
|
|
txs, err := parseTxs(obj)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blk.Txs = txs
|
|
return blk, nil
|
|
}
|
|
|
|
func parseTxs(obj zap.Object) ([]*tx.Tx, error) {
|
|
lengths := obj.ListStride(offBlkTxLengths, blkTxLenStride)
|
|
n := lengths.Len()
|
|
if n == 0 {
|
|
return nil, nil
|
|
}
|
|
blob := obj.Bytes(offBlkTxBlob)
|
|
out := make([]*tx.Tx, n)
|
|
cursor := 0
|
|
for i := 0; i < n; i++ {
|
|
size := int(lengths.Uint32(i))
|
|
if cursor+size > len(blob) {
|
|
return nil, fmt.Errorf("xsvm/block: tx %d length %d overruns blob (%d)", i, size, len(blob))
|
|
}
|
|
t, err := tx.Parse(blob[cursor : cursor+size])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("xsvm/block: parse tx %d: %w", i, err)
|
|
}
|
|
out[i] = t
|
|
cursor += size
|
|
}
|
|
return out, nil
|
|
}
|