v1.36.7: xvm native-nested composites — 2.8x X-tx build, import/export too

Consume utxo v0.5.6 + zap v1.2.3. X-chain BaseTx / ExportTx / ImportTx now build
their transferable out/in lists as native ZAP AddObjectPtr object-lists (no
per-container envelope prefix, no blob concat, no length lists) via
wire.AppendTransferable{Out,In} + TransferableXFromObject. baseTxWire composes
wire.XVMTransferOut/In directly. Removed the standalone transferableOut/InBytes
node helpers. Composite money-move build 2551ns/37allocs (byte-blob) ->
913ns/11allocs (native-nested), 2.8x. Full xvm suite green (round-trip/state/
block/executor/import/export); components-lux + wallet-x + platformvm-txs green.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
zeekay
2026-07-14 06:38:42 -07:00
co-authored by Hanzo Dev
parent 24b67abdd0
commit 97e0a0b704
8 changed files with 69 additions and 75 deletions
+2 -2
View File
@@ -143,11 +143,11 @@ require (
github.com/luxfi/timer v1.0.2
github.com/luxfi/units v1.0.0
github.com/luxfi/utils v1.2.0
github.com/luxfi/utxo v0.5.3
github.com/luxfi/utxo v0.5.6
github.com/luxfi/validators v1.2.0
github.com/luxfi/vm v1.2.7
github.com/luxfi/warp v1.24.0
github.com/luxfi/zap v1.2.2
github.com/luxfi/zap v1.2.3
github.com/luxfi/zwing v0.5.2
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
github.com/zap-proto/http v0.0.0-20260506200741-fd6047874433
+6
View File
@@ -419,6 +419,10 @@ github.com/luxfi/utils v1.2.0 h1:gtEiI7/NM6PQ/OasEpH0PvB+e5hIS/tpum9r64pYjMc=
github.com/luxfi/utils v1.2.0/go.mod h1:T2OCKT1xG9jtKR/gyJQoSkticzrE9WFQ8eohJHGu9Fg=
github.com/luxfi/utxo v0.5.3 h1:3RJ6Ow5zYs2atd6QkhRNsW/cgmrQKz/UOFI25ULebQ8=
github.com/luxfi/utxo v0.5.3/go.mod h1:YF2r3EGm9pGviZuFBY5tX6rIIYmF+2KvFC/OkW/gqTg=
github.com/luxfi/utxo v0.5.5 h1:57iLgiP2nY6d7ljvDpSoQefNbf9+S4Wknj0k99eQ20g=
github.com/luxfi/utxo v0.5.5/go.mod h1:Qom/3mk7+9aEziXzdEGvHxTAl7lA2IsSDs+WUkFxjRY=
github.com/luxfi/utxo v0.5.6 h1:xdmHmgzhq71rVLd752g4gzl7CFeUsKpxttDdzTQGXKc=
github.com/luxfi/utxo v0.5.6/go.mod h1:Qom/3mk7+9aEziXzdEGvHxTAl7lA2IsSDs+WUkFxjRY=
github.com/luxfi/validators v1.2.0 h1:VygpiBqBAdGrfkb7xzE2yrVmnXaqE+hm8FLWdGXO7G8=
github.com/luxfi/validators v1.2.0/go.mod h1:GYLulrNXAan23ZlX7sgWVbVnLpUexeB/m2qr2ymsXok=
github.com/luxfi/version v1.0.1 h1:T/1KYWEMmsrNQk7pN7PFPAwh/7XbeX7cFAKLBqI37Sk=
@@ -429,6 +433,8 @@ github.com/luxfi/warp v1.24.0 h1:jrcJNlbOiZsAEopJMy9bSaCwI5NDZ8qgp/6sNoXqepg=
github.com/luxfi/warp v1.24.0/go.mod h1:bKvTi24JHlANsl7qkWZAVr/DsMfvwy42f+Cc9x4+Sq8=
github.com/luxfi/zap v1.2.2 h1:1WoijKzhx7P//fExdv9P9GNJS1rtpwlBzHLDh5TBc30=
github.com/luxfi/zap v1.2.2/go.mod h1:JfqII8VtVQYLLTX6obU1DP9sjGqf9L24vfug5ifh0b8=
github.com/luxfi/zap v1.2.3 h1:aLPUgXH5ITqbGRS6tFx1hMZqsB4kL6j39+4s7/JlGgs=
github.com/luxfi/zap v1.2.3/go.mod h1:JfqII8VtVQYLLTX6obU1DP9sjGqf9L24vfug5ifh0b8=
github.com/luxfi/zapcodec v1.0.1 h1:pRxLxCOi6uihQMg8A8riDjNjefU2cXZxfRVZ+obeuL8=
github.com/luxfi/zapcodec v1.0.1/go.mod h1:txrRt2JK4O76ssTxlXIwoNVsgzyZVL0ES4mlXqGNogs=
github.com/luxfi/zapdb v1.10.1 h1:XV3k4UTTKKxUMgbfC7woPXgUEIJd3P5nj2lGTQ88xeE=
+2 -1
View File
@@ -68,7 +68,8 @@
"v1.36.3",
"v1.36.4",
"v1.36.5",
"v1.36.6"
"v1.36.6",
"v1.36.7"
],
"41": [
"v1.13.2"
+1 -1
View File
@@ -77,7 +77,7 @@ var (
const (
defaultMajor = 1
defaultMinor = 36
defaultPatch = 6
defaultPatch = 7
)
func init() {
+15 -8
View File
@@ -105,24 +105,31 @@ func (t *BaseTx) serialize() ([]byte, error) {
}
// baseTxWire builds the wire.XVMBaseTx envelope from the embedded spending
// envelope: each Out/In becomes an fx-typed TransferableOut/In envelope built
// from its inner fx primitive's .Bytes().
// envelope. Each Out/In carries its AssetID (X-Chain is multi-asset) plus the
// inner fx primitive's own wire envelope (.Bytes()); NewXVMBaseTx nests them
// natively (AddObjectPtr object lists), so there is no per-container envelope
// prefix and no blob concatenation.
func (t *BaseTx) baseTxWire() ([]byte, error) {
outs := make([][]byte, len(t.Outs))
outs := make([]wire.XVMTransferOut, len(t.Outs))
for i, o := range t.Outs {
b, err := transferableOutBytes(o)
inner, err := childBytes(o.Out)
if err != nil {
return nil, err
}
outs[i] = b
outs[i] = wire.XVMTransferOut{AssetID: o.Asset.ID, Output: inner}
}
ins := make([][]byte, len(t.Ins))
ins := make([]wire.XVMTransferIn, len(t.Ins))
for i, in := range t.Ins {
b, err := transferableInBytes(in)
inner, err := childBytes(in.In)
if err != nil {
return nil, err
}
ins[i] = b
ins[i] = wire.XVMTransferIn{
TxID: in.UTXOID.TxID,
OutputIndex: in.UTXOID.OutputIndex,
AssetID: in.Asset.ID,
Input: inner,
}
}
return wire.NewXVMBaseTx(wire.XVMBaseTxInput{
NetworkID: t.NetworkID,
+20 -21
View File
@@ -33,10 +33,9 @@ type ExportTx struct {
}
const (
offExportDest = 16 // 32B
offExportOutsLen = 48 // list ptr
offExportOutsBlob = 56 // bytes ptr
sizeExport = 64
offExportDest = 16 // 32B
offExportOuts = 48 // objptr list (relOffset + count, 8 bytes)
sizeExport = 56
)
func (t *ExportTx) InitRuntime(rt *runtime.Runtime) {
@@ -67,23 +66,28 @@ func (t *ExportTx) serialize() ([]byte, error) {
if err != nil {
return nil, err
}
outs := make([][]byte, len(t.ExportedOuts))
b := zap.NewBuilder(zap.HeaderSize + sizeExport + len(env) + 256)
// Append each exported TransferableOut object inline, then an objptr list —
// native nesting, no per-output envelope prefix, no blob concat.
offs := make([]int, len(t.ExportedOuts))
for i, o := range t.ExportedOuts {
b, err := transferableOutBytes(o)
inner, err := childBytes(o.Out)
if err != nil {
return nil, err
}
outs[i] = b
offs[i] = wire.AppendTransferableOut(b, o.Asset.ID, inner)
}
b := zap.NewBuilder(zap.HeaderSize + sizeExport + len(env) + 256)
outsLenOff, outsLenCount, outsBlob := writeBlobList(b, outs)
lb := b.StartList(4)
for _, off := range offs {
lb.AddObjectPtr(off)
}
outsOff, outsLen := lb.Finish()
ob := b.StartObject(sizeExport)
ob.SetUint8(offXKind, uint8(xkindExport))
ob.SetBytes(offBaseTx, env)
ob.SetBytesFixed(offExportDest, t.DestinationChain[:])
ob.SetList(offExportOutsLen, outsLenOff, outsLenCount)
ob.SetBytes(offExportOutsBlob, outsBlob)
ob.SetList(offExportOuts, outsOff, outsLen)
ob.FinishAsRoot()
return b.Finish(), nil
}
@@ -95,16 +99,11 @@ func parseExportTx(unsignedBytes []byte, obj zap.Object) (*ExportTx, error) {
}
var destChain ids.ID
copy(destChain[:], obj.BytesFixedSlice(offExportDest, 32))
outBufs, err := readBlobList(obj, offExportOutsLen, offExportOutsBlob)
if err != nil {
return nil, err
}
outs := make([]*lux.TransferableOutput, len(outBufs))
for i, buf := range outBufs {
w, err := wire.WrapTransferableOut(buf)
if err != nil {
return nil, err
}
msg := obj.Message()
l := obj.ListStride(offExportOuts, 4)
outs := make([]*lux.TransferableOutput, l.Len())
for i := range outs {
w := wire.TransferableOutFromObject(msg, l.ObjectPtr(i))
out, err := outputFromWire(w)
if err != nil {
return nil, err
+5 -21
View File
@@ -81,27 +81,11 @@ func readBlobList(obj zap.Object, lenPtrOff, blobPtrOff int) ([][]byte, error) {
return out, nil
}
// ---- TransferableOutput / TransferableInput <-> wire envelopes ----
// transferableOutBytes builds a wire TransferableOut envelope (AssetID + inner
// fx Output) from a lux.TransferableOutput.
func transferableOutBytes(o *lux.TransferableOutput) ([]byte, error) {
inner, err := childBytes(o.Out)
if err != nil {
return nil, err
}
return wire.NewTransferableOut(o.Asset.ID, inner), nil
}
// transferableInBytes builds a wire TransferableIn envelope (UTXOID + AssetID +
// inner fx Input) from a lux.TransferableInput.
func transferableInBytes(in *lux.TransferableInput) ([]byte, error) {
inner, err := childBytes(in.In)
if err != nil {
return nil, err
}
return wire.NewTransferableIn(in.UTXOID.TxID, in.UTXOID.OutputIndex, in.Asset.ID, inner), nil
}
// ---- TransferableOutput / TransferableInput <-> wire ----
//
// Build side lives in baseTxWire (BaseTx composes XVMTransferOut/In directly so
// the whole tx nests in one buffer). These reconstruct the node types from the
// nested wire accessors on the read side.
// outputFromWire reconstructs a lux.TransferableOutput from a parsed wire
// TransferableOut, dispatching the inner fx Output on its own discriminator.
+18 -21
View File
@@ -34,10 +34,9 @@ type ImportTx struct {
}
const (
offImportSource = 16 // 32B
offImportInsLen = 48 // list ptr
offImportInsBlob = 56 // bytes ptr
sizeImport = 64
offImportSource = 16 // 32B
offImportIns = 48 // objptr list (relOffset + count, 8 bytes)
sizeImport = 56
)
// InputUTXOs track which UTXOs this transaction is consuming.
@@ -88,23 +87,26 @@ func (t *ImportTx) serialize() ([]byte, error) {
if err != nil {
return nil, err
}
ins := make([][]byte, len(t.ImportedIns))
b := zap.NewBuilder(zap.HeaderSize + sizeImport + len(env) + 256)
offs := make([]int, len(t.ImportedIns))
for i, in := range t.ImportedIns {
b, err := transferableInBytes(in)
inner, err := childBytes(in.In)
if err != nil {
return nil, err
}
ins[i] = b
offs[i] = wire.AppendTransferableIn(b, in.UTXOID.TxID, in.UTXOID.OutputIndex, in.Asset.ID, inner)
}
b := zap.NewBuilder(zap.HeaderSize + sizeImport + len(env) + 256)
insLenOff, insLenCount, insBlob := writeBlobList(b, ins)
lb := b.StartList(4)
for _, off := range offs {
lb.AddObjectPtr(off)
}
insOff, insLen := lb.Finish()
ob := b.StartObject(sizeImport)
ob.SetUint8(offXKind, uint8(xkindImport))
ob.SetBytes(offBaseTx, env)
ob.SetBytesFixed(offImportSource, t.SourceChain[:])
ob.SetList(offImportInsLen, insLenOff, insLenCount)
ob.SetBytes(offImportInsBlob, insBlob)
ob.SetList(offImportIns, insOff, insLen)
ob.FinishAsRoot()
return b.Finish(), nil
}
@@ -116,16 +118,11 @@ func parseImportTx(unsignedBytes []byte, obj zap.Object) (*ImportTx, error) {
}
var sourceChain ids.ID
copy(sourceChain[:], obj.BytesFixedSlice(offImportSource, 32))
inBufs, err := readBlobList(obj, offImportInsLen, offImportInsBlob)
if err != nil {
return nil, err
}
ins := make([]*lux.TransferableInput, len(inBufs))
for i, buf := range inBufs {
w, err := wire.WrapTransferableIn(buf)
if err != nil {
return nil, err
}
msg := obj.Message()
l := obj.ListStride(offImportIns, 4)
ins := make([]*lux.TransferableInput, l.Len())
for i := range ins {
w := wire.TransferableInFromObject(msg, l.ObjectPtr(i))
in, err := inputFromWire(w)
if err != nil {
return nil, err