mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
platformvm/txs/zap_native: 5 batch-3 tx types composing the new primitives (LP-023 Phase 1)
TxKindBaseFull = 11 → BaseTxFull
TxKindAddPermissionlessValidator = 12 → AddPermissionlessValidatorTx
TxKindImport = 13 → ImportTx
TxKindExport = 14 → ExportTx
TxKindCreateChain = 15 → CreateChainTx
Sizes (fixed section, schema v3):
BaseTxFull = 85
ImportTx = 125
ExportTx = 125
AddPermissionlessValidatorTx = 381
CreateChainTx = 221
Design highlights:
- BaseTxFull is the real P-chain spending envelope (Outs + Ins +
Credentials + 2 shared arrays + Memo). The batch-2 BaseTx
(TxKindBase) remains as the minimal metadata envelope for places
that need only {NetworkID, BlockchainID, Memo} without spending
state. Both kinds are first-class; they are NOT alternatives.
- Import / Export use a SINGLE combined Ins (or Outs) list with a
header marker (ImportedInsStart/Count or ExportedOutsStart/Count)
identifying the cross-chain slice. This collapses what would have
been two parallel sig-index arrays into one shared array — fewer
pointer pairs, no rebase bookkeeping. The slice-based indexing is
byte-identical for the imported/exported half because they
reference the same shared array.
- AddPermissionlessValidatorTx carries two single-address Owner stubs
(validation rewards, delegation rewards) inline in the fixed
section. The same OwnerStub type underlies CreateChainTx.Owner.
Multi-address Owner ships in batch 4 along with the AddressList
primitive; current callers needing multi-addr flow through the
legacy codec gate.
- CreateChainTx embeds a 32-byte WarpMessageHash (sha256 of the
originating Warp commit; zero when the chain is being created
directly without a cross-network commit). The Warp message BODY
is NOT embedded — it lives in the signed Warp envelope outside
this tx. Hash-only embedding keeps the unsigned-tx bytes stable
and the Warp envelope separately verifiable.
All five tx types follow the v3 invariant: TxKind@0, Wrap* rejects
mismatched discriminator with ErrWrongTxKind. Cross-confusion test
expansion verifies the new tx types in pairwise rejection (7
additional scenarios cover the batch 1+2+3 surface).
Deferred to batch 4 (no new primitives needed): the remaining ~18
classical platformvm tx types (CreateNetwork, AddDelegator,
AddPermissionlessDelegator, etc.) reuse the existing batch-3
primitives; their landings are mechanical.
go test -race ./vms/platformvm/txs/zap_native/...
ok github.com/luxfi/node/vms/platformvm/txs/zap_native 1.488s
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package zap_native
|
||||
|
||||
import (
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
// AddPermissionlessValidatorTx is the v3 register-permissionless-validator
|
||||
// envelope. It carries the BaseTxFull spending state, the validator
|
||||
// identity (NodeID + bond stake metadata + signer), and two nested Owner
|
||||
// stubs (validation rewards owner + delegation rewards owner). Both owners
|
||||
// are single-address stubs in v3 (matching TransferChainOwnershipTx /
|
||||
// OutputList semantics). Multi-address owners flow through legacy.
|
||||
//
|
||||
// Fixed-section layout (size 269 bytes):
|
||||
//
|
||||
// TxKind uint8 @ 0
|
||||
// NetworkID uint32 @ 1
|
||||
// BlockchainID 32B @ 5
|
||||
// OutsList 8B @ 37
|
||||
// InsList 8B @ 45
|
||||
// CredsList 8B @ 53
|
||||
// SigIndicesArr 8B @ 61
|
||||
// SigArr 8B @ 69
|
||||
// Memo 8B @ 77
|
||||
// NodeID 20B @ 85 (ids.NodeID)
|
||||
// StakeStart uint64 @ 105
|
||||
// StakeEnd uint64 @ 113
|
||||
// StakeWeight uint64 @ 121
|
||||
// StakeAssetID 32B @ 129
|
||||
// StakeAmount uint64 @ 161
|
||||
// BLSPublicKey 48B @ 169 (bls.PublicKeyLen)
|
||||
// BLSProofOfPossession 96B @ 217 (bls.SignatureLen)
|
||||
// ValidationRewardsOwnerThresh uint32 @ 313
|
||||
// ValidationRewardsOwnerLT uint64 @ 317
|
||||
// ValidationRewardsOwnerAddr 20B @ 325
|
||||
// DelegationRewardsOwnerThresh uint32 @ 345
|
||||
// DelegationRewardsOwnerLT uint64 @ 349
|
||||
// DelegationRewardsOwnerAddr 20B @ 357
|
||||
// DelegationShares uint32 @ 377
|
||||
// Size = 381
|
||||
const (
|
||||
OffsetAPVTx_NetworkID = 1
|
||||
OffsetAPVTx_BlockchainID = 5
|
||||
OffsetAPVTx_OutsList = 37
|
||||
OffsetAPVTx_InsList = 45
|
||||
OffsetAPVTx_CredsList = 53
|
||||
OffsetAPVTx_SigIndicesArr = 61
|
||||
OffsetAPVTx_SigArr = 69
|
||||
OffsetAPVTx_Memo = 77
|
||||
OffsetAPVTx_NodeID = 85
|
||||
OffsetAPVTx_StakeStart = 105
|
||||
OffsetAPVTx_StakeEnd = 113
|
||||
OffsetAPVTx_StakeWeight = 121
|
||||
OffsetAPVTx_StakeAssetID = 129
|
||||
OffsetAPVTx_StakeAmount = 161
|
||||
OffsetAPVTx_BLSPublicKey = 169
|
||||
OffsetAPVTx_BLSProofOfPossession = OffsetAPVTx_BLSPublicKey + bls.PublicKeyLen
|
||||
OffsetAPVTx_ValidationRewardsOwnerThreshold = OffsetAPVTx_BLSProofOfPossession + bls.SignatureLen
|
||||
OffsetAPVTx_ValidationRewardsOwnerLocktime = OffsetAPVTx_ValidationRewardsOwnerThreshold + 4
|
||||
OffsetAPVTx_ValidationRewardsOwnerAddress = OffsetAPVTx_ValidationRewardsOwnerLocktime + 8
|
||||
OffsetAPVTx_DelegationRewardsOwnerThreshold = OffsetAPVTx_ValidationRewardsOwnerAddress + ids.ShortIDLen
|
||||
OffsetAPVTx_DelegationRewardsOwnerLocktime = OffsetAPVTx_DelegationRewardsOwnerThreshold + 4
|
||||
OffsetAPVTx_DelegationRewardsOwnerAddress = OffsetAPVTx_DelegationRewardsOwnerLocktime + 8
|
||||
OffsetAPVTx_DelegationShares = OffsetAPVTx_DelegationRewardsOwnerAddress + ids.ShortIDLen
|
||||
SizeAddPermissionlessValidatorTx = OffsetAPVTx_DelegationShares + 4
|
||||
)
|
||||
|
||||
// AddPermissionlessValidatorTx is the zero-copy accessor.
|
||||
type AddPermissionlessValidatorTx struct {
|
||||
msg *zap.Message
|
||||
obj zap.Object
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) NetworkID() uint32 {
|
||||
return t.obj.Uint32(OffsetAPVTx_NetworkID)
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) BlockchainID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetAPVTx_BlockchainID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) Outs() OutputList {
|
||||
return OutputListView(t.obj, OffsetAPVTx_OutsList)
|
||||
}
|
||||
func (t AddPermissionlessValidatorTx) Ins() InputList {
|
||||
return InputListView(t.obj, OffsetAPVTx_InsList)
|
||||
}
|
||||
func (t AddPermissionlessValidatorTx) Credentials() CredentialList {
|
||||
return CredentialListView(t.obj, OffsetAPVTx_CredsList)
|
||||
}
|
||||
func (t AddPermissionlessValidatorTx) SigIndicesArray() SigIndicesArray {
|
||||
return SigIndicesArrayView(t.obj, OffsetAPVTx_SigIndicesArr)
|
||||
}
|
||||
func (t AddPermissionlessValidatorTx) SignatureArray() SignatureArray {
|
||||
return SignatureArrayView(t.obj, OffsetAPVTx_SigArr)
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) Memo() []byte {
|
||||
return t.obj.Bytes(OffsetAPVTx_Memo)
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) NodeID() ids.NodeID {
|
||||
var out ids.NodeID
|
||||
for i := 0; i < ids.ShortIDLen; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetAPVTx_NodeID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) StakeStart() uint64 {
|
||||
return t.obj.Uint64(OffsetAPVTx_StakeStart)
|
||||
}
|
||||
func (t AddPermissionlessValidatorTx) StakeEnd() uint64 {
|
||||
return t.obj.Uint64(OffsetAPVTx_StakeEnd)
|
||||
}
|
||||
func (t AddPermissionlessValidatorTx) StakeWeight() uint64 {
|
||||
return t.obj.Uint64(OffsetAPVTx_StakeWeight)
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) StakeAssetID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetAPVTx_StakeAssetID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) StakeAmount() uint64 {
|
||||
return t.obj.Uint64(OffsetAPVTx_StakeAmount)
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) BLSPublicKey() [bls.PublicKeyLen]byte {
|
||||
var out [bls.PublicKeyLen]byte
|
||||
for i := 0; i < bls.PublicKeyLen; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetAPVTx_BLSPublicKey + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) BLSProofOfPossession() [bls.SignatureLen]byte {
|
||||
var out [bls.SignatureLen]byte
|
||||
for i := 0; i < bls.SignatureLen; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetAPVTx_BLSProofOfPossession + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ValidationRewardsOwner returns the (threshold, locktime, address) tuple
|
||||
// for the validation-rewards owner. Single-address stub form (v3).
|
||||
func (t AddPermissionlessValidatorTx) ValidationRewardsOwner() (uint32, uint64, ids.ShortID) {
|
||||
threshold := t.obj.Uint32(OffsetAPVTx_ValidationRewardsOwnerThreshold)
|
||||
locktime := t.obj.Uint64(OffsetAPVTx_ValidationRewardsOwnerLocktime)
|
||||
var addr ids.ShortID
|
||||
for i := 0; i < ids.ShortIDLen; i++ {
|
||||
addr[i] = t.obj.Uint8(OffsetAPVTx_ValidationRewardsOwnerAddress + i)
|
||||
}
|
||||
return threshold, locktime, addr
|
||||
}
|
||||
|
||||
// DelegationRewardsOwner mirrors ValidationRewardsOwner for the delegation
|
||||
// rewards owner stub.
|
||||
func (t AddPermissionlessValidatorTx) DelegationRewardsOwner() (uint32, uint64, ids.ShortID) {
|
||||
threshold := t.obj.Uint32(OffsetAPVTx_DelegationRewardsOwnerThreshold)
|
||||
locktime := t.obj.Uint64(OffsetAPVTx_DelegationRewardsOwnerLocktime)
|
||||
var addr ids.ShortID
|
||||
for i := 0; i < ids.ShortIDLen; i++ {
|
||||
addr[i] = t.obj.Uint8(OffsetAPVTx_DelegationRewardsOwnerAddress + i)
|
||||
}
|
||||
return threshold, locktime, addr
|
||||
}
|
||||
|
||||
// DelegationShares returns the delegation-shares percentage in units of
|
||||
// reward.PercentDenominator.
|
||||
func (t AddPermissionlessValidatorTx) DelegationShares() uint32 {
|
||||
return t.obj.Uint32(OffsetAPVTx_DelegationShares)
|
||||
}
|
||||
|
||||
func (t AddPermissionlessValidatorTx) Bytes() []byte { return t.msg.Bytes() }
|
||||
func (t AddPermissionlessValidatorTx) IsZero() bool { return t.msg == nil }
|
||||
|
||||
func WrapAddPermissionlessValidatorTx(b []byte) (AddPermissionlessValidatorTx, error) {
|
||||
msg, err := zap.Parse(b)
|
||||
if err != nil {
|
||||
return AddPermissionlessValidatorTx{}, err
|
||||
}
|
||||
obj := msg.Root()
|
||||
if TxKind(obj.Uint8(OffsetTxKind)) != TxKindAddPermissionlessValidator {
|
||||
return AddPermissionlessValidatorTx{}, ErrWrongTxKind
|
||||
}
|
||||
return AddPermissionlessValidatorTx{msg: msg, obj: obj}, nil
|
||||
}
|
||||
|
||||
// OwnerStub is the v3 single-address owner stub used by APV's two
|
||||
// rewards-owner fields.
|
||||
type OwnerStub struct {
|
||||
Threshold uint32
|
||||
Locktime uint64
|
||||
Address ids.ShortID
|
||||
}
|
||||
|
||||
// AddPermissionlessValidatorTxInput is the constructor input.
|
||||
type AddPermissionlessValidatorTxInput struct {
|
||||
NetworkID uint32
|
||||
BlockchainID ids.ID
|
||||
Outs []OutputListEntry
|
||||
Ins []InputListEntry
|
||||
Credentials []CredentialListEntry
|
||||
Memo []byte
|
||||
|
||||
NodeID ids.NodeID
|
||||
StakeStart uint64
|
||||
StakeEnd uint64
|
||||
StakeWeight uint64
|
||||
StakeAssetID ids.ID
|
||||
StakeAmount uint64
|
||||
BLSPublicKey [bls.PublicKeyLen]byte
|
||||
BLSProofOfPossession [bls.SignatureLen]byte
|
||||
|
||||
ValidationRewardsOwner OwnerStub
|
||||
DelegationRewardsOwner OwnerStub
|
||||
DelegationShares uint32
|
||||
}
|
||||
|
||||
func NewAddPermissionlessValidatorTx(in AddPermissionlessValidatorTxInput) AddPermissionlessValidatorTx {
|
||||
cap := zap.HeaderSize + 16 + SizeAddPermissionlessValidatorTx
|
||||
cap += len(in.Outs) * SizeTransferableOutput
|
||||
cap += len(in.Ins) * SizeTransferableInput
|
||||
cap += len(in.Credentials) * SizeCredential
|
||||
cap += len(in.Memo)
|
||||
b := zap.NewBuilder(cap)
|
||||
|
||||
outsOff, outsCount := WriteOutputList(b, in.Outs)
|
||||
insOff, insCount, sigIndices := WriteInputList(b, in.Ins)
|
||||
credsOff, credsCount, sigBlobs := WriteCredentialList(b, in.Credentials)
|
||||
sigIdxArrOff, sigIdxArrCount := WriteSigIndicesArray(b, sigIndices)
|
||||
sigArrOff, sigArrCount := WriteSignatureArray(b, sigBlobs)
|
||||
|
||||
ob := b.StartObject(SizeAddPermissionlessValidatorTx)
|
||||
ob.SetUint8(OffsetTxKind, uint8(TxKindAddPermissionlessValidator))
|
||||
ob.SetUint32(OffsetAPVTx_NetworkID, in.NetworkID)
|
||||
for i := 0; i < 32; i++ {
|
||||
ob.SetUint8(OffsetAPVTx_BlockchainID+i, in.BlockchainID[i])
|
||||
ob.SetUint8(OffsetAPVTx_StakeAssetID+i, in.StakeAssetID[i])
|
||||
}
|
||||
ob.SetList(OffsetAPVTx_OutsList, outsOff, outsCount)
|
||||
ob.SetList(OffsetAPVTx_InsList, insOff, insCount)
|
||||
ob.SetList(OffsetAPVTx_CredsList, credsOff, credsCount)
|
||||
ob.SetList(OffsetAPVTx_SigIndicesArr, sigIdxArrOff, sigIdxArrCount)
|
||||
ob.SetList(OffsetAPVTx_SigArr, sigArrOff, sigArrCount)
|
||||
ob.SetBytes(OffsetAPVTx_Memo, in.Memo)
|
||||
for i := 0; i < ids.ShortIDLen; i++ {
|
||||
ob.SetUint8(OffsetAPVTx_NodeID+i, in.NodeID[i])
|
||||
ob.SetUint8(OffsetAPVTx_ValidationRewardsOwnerAddress+i, in.ValidationRewardsOwner.Address[i])
|
||||
ob.SetUint8(OffsetAPVTx_DelegationRewardsOwnerAddress+i, in.DelegationRewardsOwner.Address[i])
|
||||
}
|
||||
ob.SetUint64(OffsetAPVTx_StakeStart, in.StakeStart)
|
||||
ob.SetUint64(OffsetAPVTx_StakeEnd, in.StakeEnd)
|
||||
ob.SetUint64(OffsetAPVTx_StakeWeight, in.StakeWeight)
|
||||
ob.SetUint64(OffsetAPVTx_StakeAmount, in.StakeAmount)
|
||||
for i := 0; i < bls.PublicKeyLen; i++ {
|
||||
ob.SetUint8(OffsetAPVTx_BLSPublicKey+i, in.BLSPublicKey[i])
|
||||
}
|
||||
for i := 0; i < bls.SignatureLen; i++ {
|
||||
ob.SetUint8(OffsetAPVTx_BLSProofOfPossession+i, in.BLSProofOfPossession[i])
|
||||
}
|
||||
ob.SetUint32(OffsetAPVTx_ValidationRewardsOwnerThreshold, in.ValidationRewardsOwner.Threshold)
|
||||
ob.SetUint64(OffsetAPVTx_ValidationRewardsOwnerLocktime, in.ValidationRewardsOwner.Locktime)
|
||||
ob.SetUint32(OffsetAPVTx_DelegationRewardsOwnerThreshold, in.DelegationRewardsOwner.Threshold)
|
||||
ob.SetUint64(OffsetAPVTx_DelegationRewardsOwnerLocktime, in.DelegationRewardsOwner.Locktime)
|
||||
ob.SetUint32(OffsetAPVTx_DelegationShares, in.DelegationShares)
|
||||
ob.FinishAsRoot()
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return AddPermissionlessValidatorTx{msg: msg, obj: msg.Root()}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package zap_native
|
||||
|
||||
import (
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
// BaseTxFull is the full P-chain spending envelope: NetworkID + BlockchainID
|
||||
// + Outs + Ins + Credentials + Memo. Higher-level tx types (Import/Export/
|
||||
// AddPermissionlessValidator/CreateChain) embed this. The minimal
|
||||
// metadata-only BaseTx (TxKindBase) remains for places that need only
|
||||
// {NetworkID, BlockchainID, Memo} without spending state.
|
||||
//
|
||||
// Fixed-section layout (size 73 bytes; uint64 reads alignment-tolerant):
|
||||
//
|
||||
// TxKind uint8 @ 0
|
||||
// NetworkID uint32 @ 1
|
||||
// BlockchainID 32B @ 5
|
||||
// OutsListRel uint32 @ 37 (list pointer; (offset,count))
|
||||
// OutsListLen uint32 @ 41
|
||||
// InsListRel uint32 @ 45
|
||||
// InsListLen uint32 @ 49
|
||||
// CredsListRel uint32 @ 53
|
||||
// CredsListLen uint32 @ 57
|
||||
// SigIndicesArrRel uint32 @ 61 (shared uint32 array; InputList slices here)
|
||||
// SigIndicesArrLen uint32 @ 65
|
||||
// SigArrRel uint32 @ 69 (shared signature blob array; CredentialList slices)
|
||||
// SigArrLen uint32 @ 73
|
||||
// Memo bytes @ 77 (relOffset + length, 8 bytes)
|
||||
// (fixed-section size 85; the 4 list-pointer pairs are 8 bytes each,
|
||||
// stored as 4-byte rel + 4-byte length consistent with zap.Object.List)
|
||||
//
|
||||
// Layout summary: header + 5 lists/arrays (each is a 4-byte relOffset and a
|
||||
// 4-byte length, 40 bytes total for the 5 pointers) + Memo (8 bytes).
|
||||
//
|
||||
// Total fixed section = 1 (TxKind) + 4 (NetworkID) + 32 (BlockchainID) +
|
||||
// 5*8 (4 lists + 1 array pair) + 8 (Memo) = 85 bytes. But the 5 list+arr
|
||||
// pointers are split into 5×(4+4) = 40 bytes, beginning at offset 37.
|
||||
const (
|
||||
OffsetBaseTxFull_NetworkID = 1
|
||||
OffsetBaseTxFull_BlockchainID = 5
|
||||
OffsetBaseTxFull_OutsList = 37 // list pointer (8 bytes)
|
||||
OffsetBaseTxFull_InsList = 45 // list pointer (8 bytes)
|
||||
OffsetBaseTxFull_CredsList = 53 // list pointer (8 bytes)
|
||||
OffsetBaseTxFull_SigIndicesArr = 61 // list pointer (8 bytes)
|
||||
OffsetBaseTxFull_SigArr = 69 // list pointer (8 bytes)
|
||||
OffsetBaseTxFull_Memo = 77 // bytes (8 bytes)
|
||||
SizeBaseTxFull = 85
|
||||
)
|
||||
|
||||
// BaseTxFull is a zero-copy typed accessor over a ZAP buffer carrying a v3
|
||||
// full P-chain spending tx.
|
||||
type BaseTxFull struct {
|
||||
msg *zap.Message
|
||||
obj zap.Object
|
||||
}
|
||||
|
||||
// NetworkID returns the network ID the tx targets.
|
||||
func (t BaseTxFull) NetworkID() uint32 {
|
||||
return t.obj.Uint32(OffsetBaseTxFull_NetworkID)
|
||||
}
|
||||
|
||||
// BlockchainID returns the chain ID the tx executes against.
|
||||
func (t BaseTxFull) BlockchainID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetBaseTxFull_BlockchainID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Outs returns the OutputList view.
|
||||
func (t BaseTxFull) Outs() OutputList {
|
||||
return OutputListView(t.obj, OffsetBaseTxFull_OutsList)
|
||||
}
|
||||
|
||||
// Ins returns the InputList view.
|
||||
func (t BaseTxFull) Ins() InputList {
|
||||
return InputListView(t.obj, OffsetBaseTxFull_InsList)
|
||||
}
|
||||
|
||||
// Credentials returns the CredentialList view.
|
||||
func (t BaseTxFull) Credentials() CredentialList {
|
||||
return CredentialListView(t.obj, OffsetBaseTxFull_CredsList)
|
||||
}
|
||||
|
||||
// SigIndicesArray returns the shared uint32 sig-index array view that the
|
||||
// InputList entries slice into.
|
||||
func (t BaseTxFull) SigIndicesArray() SigIndicesArray {
|
||||
return SigIndicesArrayView(t.obj, OffsetBaseTxFull_SigIndicesArr)
|
||||
}
|
||||
|
||||
// SignatureArray returns the shared signature blob array view that the
|
||||
// CredentialList entries slice into.
|
||||
func (t BaseTxFull) SignatureArray() SignatureArray {
|
||||
return SignatureArrayView(t.obj, OffsetBaseTxFull_SigArr)
|
||||
}
|
||||
|
||||
// Memo returns the memo bytes.
|
||||
//
|
||||
// READ-ONLY: aliases the underlying ZAP buffer. Mutation corrupts the
|
||||
// parsed tx and breaks TxID = hash(buffer). Use append([]byte(nil), m...)
|
||||
// to take ownership.
|
||||
func (t BaseTxFull) Memo() []byte {
|
||||
return t.obj.Bytes(OffsetBaseTxFull_Memo)
|
||||
}
|
||||
|
||||
func (t BaseTxFull) Bytes() []byte { return t.msg.Bytes() }
|
||||
func (t BaseTxFull) IsZero() bool { return t.msg == nil }
|
||||
|
||||
// WrapBaseTxFull parses a ZAP buffer into a typed BaseTxFull accessor.
|
||||
//
|
||||
// Returns ErrWrongTxKind if the discriminator does not match TxKindBaseFull.
|
||||
func WrapBaseTxFull(b []byte) (BaseTxFull, error) {
|
||||
msg, err := zap.Parse(b)
|
||||
if err != nil {
|
||||
return BaseTxFull{}, err
|
||||
}
|
||||
obj := msg.Root()
|
||||
if TxKind(obj.Uint8(OffsetTxKind)) != TxKindBaseFull {
|
||||
return BaseTxFull{}, ErrWrongTxKind
|
||||
}
|
||||
return BaseTxFull{msg: msg, obj: obj}, nil
|
||||
}
|
||||
|
||||
// BaseTxFullInput is the constructor input for NewBaseTxFull. The lists are
|
||||
// converted to ZAP wire form inside the builder; no separate writer call
|
||||
// needed by the caller.
|
||||
type BaseTxFullInput struct {
|
||||
NetworkID uint32
|
||||
BlockchainID ids.ID
|
||||
Outs []OutputListEntry
|
||||
Ins []InputListEntry
|
||||
Credentials []CredentialListEntry
|
||||
Memo []byte
|
||||
}
|
||||
|
||||
// NewBaseTxFull builds a v3 BaseTxFull into a fresh ZAP buffer. Outs/Ins/
|
||||
// Credentials may each be empty (the corresponding list pointer is
|
||||
// (0, 0)).
|
||||
func NewBaseTxFull(in BaseTxFullInput) BaseTxFull {
|
||||
// Capacity estimate: header + fixed section + list payloads + memo.
|
||||
cap := zap.HeaderSize + 16 + SizeBaseTxFull
|
||||
cap += len(in.Outs) * SizeTransferableOutput
|
||||
cap += len(in.Ins) * SizeTransferableInput
|
||||
cap += len(in.Credentials) * SizeCredential
|
||||
cap += len(in.Memo)
|
||||
b := zap.NewBuilder(cap)
|
||||
|
||||
// Write all lists FIRST so they're positioned in the variable section
|
||||
// before the parent object's fixed section. The parent's list pointers
|
||||
// will then be backward (negative) — ZAP's signed-int32 list/Object
|
||||
// decoding accepts that, post-F1.
|
||||
outsOff, outsCount := WriteOutputList(b, in.Outs)
|
||||
insOff, insCount, sigIndices := WriteInputList(b, in.Ins)
|
||||
credsOff, credsCount, sigBlobs := WriteCredentialList(b, in.Credentials)
|
||||
sigIdxArrOff, sigIdxArrCount := WriteSigIndicesArray(b, sigIndices)
|
||||
sigArrOff, sigArrCount := WriteSignatureArray(b, sigBlobs)
|
||||
|
||||
ob := b.StartObject(SizeBaseTxFull)
|
||||
ob.SetUint8(OffsetTxKind, uint8(TxKindBaseFull))
|
||||
ob.SetUint32(OffsetBaseTxFull_NetworkID, in.NetworkID)
|
||||
for i := 0; i < 32; i++ {
|
||||
ob.SetUint8(OffsetBaseTxFull_BlockchainID+i, in.BlockchainID[i])
|
||||
}
|
||||
ob.SetList(OffsetBaseTxFull_OutsList, outsOff, outsCount)
|
||||
ob.SetList(OffsetBaseTxFull_InsList, insOff, insCount)
|
||||
ob.SetList(OffsetBaseTxFull_CredsList, credsOff, credsCount)
|
||||
ob.SetList(OffsetBaseTxFull_SigIndicesArr, sigIdxArrOff, sigIdxArrCount)
|
||||
ob.SetList(OffsetBaseTxFull_SigArr, sigArrOff, sigArrCount)
|
||||
ob.SetBytes(OffsetBaseTxFull_Memo, in.Memo)
|
||||
ob.FinishAsRoot()
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return BaseTxFull{msg: msg, obj: msg.Root()}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package zap_native
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// Helper to build a non-trivial sample outs/ins/creds set used by multiple
|
||||
// round-trip tests.
|
||||
func sampleOuts() []OutputListEntry {
|
||||
return []OutputListEntry{
|
||||
{AssetID: ids.ID{0xa1}, Amount: 100, Threshold: 1, Locktime: 0, OwnerAddress: ids.ShortID{0x11}},
|
||||
{AssetID: ids.ID{0xa2}, Amount: 200, Threshold: 1, Locktime: 1_900_000_000, OwnerAddress: ids.ShortID{0x22}},
|
||||
}
|
||||
}
|
||||
|
||||
func sampleIns() []InputListEntry {
|
||||
return []InputListEntry{
|
||||
{TxID: ids.ID{0x01}, OutputIndex: 0, AssetID: ids.ID{0xa1}, Amount: 100, SigIndices: []uint32{0}},
|
||||
{TxID: ids.ID{0x02}, OutputIndex: 1, AssetID: ids.ID{0xa2}, Amount: 200, SigIndices: []uint32{0, 1}},
|
||||
}
|
||||
}
|
||||
|
||||
func sampleCreds() []CredentialListEntry {
|
||||
var s0, s1, s2 [SigBlobSize]byte
|
||||
for i := range s0 {
|
||||
s0[i] = 0xA0
|
||||
s1[i] = 0xB0
|
||||
s2[i] = 0xC0
|
||||
}
|
||||
return []CredentialListEntry{
|
||||
{Sigs: [][SigBlobSize]byte{s0}},
|
||||
{Sigs: [][SigBlobSize]byte{s1, s2}},
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseTxFullRoundTrip(t *testing.T) {
|
||||
in := BaseTxFullInput{
|
||||
NetworkID: 1337,
|
||||
BlockchainID: ids.ID{0xc1},
|
||||
Outs: sampleOuts(),
|
||||
Ins: sampleIns(),
|
||||
Credentials: sampleCreds(),
|
||||
Memo: []byte("phase C canary"),
|
||||
}
|
||||
|
||||
tx := NewBaseTxFull(in)
|
||||
if !IsZAPBytes(tx.Bytes()) {
|
||||
t.Fatal("BaseTxFull bytes not ZAP-formatted")
|
||||
}
|
||||
if tx.NetworkID() != in.NetworkID {
|
||||
t.Errorf("NetworkID round-trip")
|
||||
}
|
||||
if tx.BlockchainID() != in.BlockchainID {
|
||||
t.Errorf("BlockchainID round-trip")
|
||||
}
|
||||
if !bytes.Equal(tx.Memo(), in.Memo) {
|
||||
t.Errorf("Memo round-trip")
|
||||
}
|
||||
if tx.Outs().Len() != len(in.Outs) {
|
||||
t.Fatalf("Outs.Len() = %d, want %d", tx.Outs().Len(), len(in.Outs))
|
||||
}
|
||||
if tx.Ins().Len() != len(in.Ins) {
|
||||
t.Fatalf("Ins.Len() = %d, want %d", tx.Ins().Len(), len(in.Ins))
|
||||
}
|
||||
if tx.Credentials().Len() != len(in.Credentials) {
|
||||
t.Fatalf("Credentials.Len() = %d, want %d", tx.Credentials().Len(), len(in.Credentials))
|
||||
}
|
||||
if tx.SigIndicesArray().Len() != 3 {
|
||||
t.Fatalf("SigIndicesArray.Len() = %d, want 3", tx.SigIndicesArray().Len())
|
||||
}
|
||||
if tx.SignatureArray().Len() != 3 {
|
||||
t.Fatalf("SignatureArray.Len() = %d, want 3", tx.SignatureArray().Len())
|
||||
}
|
||||
|
||||
tx2, err := WrapBaseTxFull(tx.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Wrap failed: %v", err)
|
||||
}
|
||||
if tx2.NetworkID() != in.NetworkID || tx2.BlockchainID() != in.BlockchainID {
|
||||
t.Fatal("wrap-round-trip mismatch")
|
||||
}
|
||||
// Cross-confusion: WrapBaseTx (the metadata-only stub) must reject.
|
||||
if _, err := WrapBaseTx(tx.Bytes()); err != ErrWrongTxKind {
|
||||
t.Fatalf("WrapBaseTx on BaseTxFull bytes: want ErrWrongTxKind, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportTxRoundTrip(t *testing.T) {
|
||||
in := ImportTxInput{
|
||||
NetworkID: 1337,
|
||||
BlockchainID: ids.ID{0xc1},
|
||||
SourceNetwork: ids.ID{0xd1, 0xd2},
|
||||
Outs: sampleOuts(),
|
||||
LocalIns: sampleIns(),
|
||||
ImportedIns: []InputListEntry{
|
||||
{TxID: ids.ID{0x03}, OutputIndex: 0, AssetID: ids.ID{0xa3}, Amount: 999, SigIndices: []uint32{0}},
|
||||
},
|
||||
Credentials: sampleCreds(),
|
||||
Memo: []byte("import"),
|
||||
}
|
||||
|
||||
tx := NewImportTx(in)
|
||||
if tx.SourceNetwork() != in.SourceNetwork {
|
||||
t.Errorf("SourceNetwork round-trip")
|
||||
}
|
||||
wantStart, wantCount := uint32(len(in.LocalIns)), uint32(len(in.ImportedIns))
|
||||
gotStart, gotCount := tx.ImportedInsRange()
|
||||
if gotStart != wantStart || gotCount != wantCount {
|
||||
t.Fatalf("ImportedInsRange = (%d,%d), want (%d,%d)", gotStart, gotCount, wantStart, wantCount)
|
||||
}
|
||||
// Combined Ins length includes both local and imported.
|
||||
if tx.Ins().Len() != len(in.LocalIns)+len(in.ImportedIns) {
|
||||
t.Fatalf("Ins.Len() = %d, want %d", tx.Ins().Len(), len(in.LocalIns)+len(in.ImportedIns))
|
||||
}
|
||||
|
||||
tx2, err := WrapImportTx(tx.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Wrap failed: %v", err)
|
||||
}
|
||||
if tx2.SourceNetwork() != in.SourceNetwork {
|
||||
t.Fatal("wrap-round-trip mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportTxRoundTrip(t *testing.T) {
|
||||
in := ExportTxInput{
|
||||
NetworkID: 1337,
|
||||
BlockchainID: ids.ID{0xc1},
|
||||
DestinationNetwork: ids.ID{0xe1, 0xe2},
|
||||
LocalOuts: sampleOuts(),
|
||||
ExportedOuts: []OutputListEntry{
|
||||
{AssetID: ids.ID{0xa9}, Amount: 500, Threshold: 1, Locktime: 0, OwnerAddress: ids.ShortID{0x99}},
|
||||
},
|
||||
Ins: sampleIns(),
|
||||
Credentials: sampleCreds(),
|
||||
Memo: []byte("export"),
|
||||
}
|
||||
|
||||
tx := NewExportTx(in)
|
||||
if tx.DestinationNetwork() != in.DestinationNetwork {
|
||||
t.Errorf("DestinationNetwork round-trip")
|
||||
}
|
||||
wantStart, wantCount := uint32(len(in.LocalOuts)), uint32(len(in.ExportedOuts))
|
||||
gotStart, gotCount := tx.ExportedOutsRange()
|
||||
if gotStart != wantStart || gotCount != wantCount {
|
||||
t.Fatalf("ExportedOutsRange = (%d,%d), want (%d,%d)", gotStart, gotCount, wantStart, wantCount)
|
||||
}
|
||||
|
||||
tx2, err := WrapExportTx(tx.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Wrap failed: %v", err)
|
||||
}
|
||||
if tx2.DestinationNetwork() != in.DestinationNetwork {
|
||||
t.Fatal("wrap-round-trip mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPermissionlessValidatorTxRoundTrip(t *testing.T) {
|
||||
var blsPub [bls.PublicKeyLen]byte
|
||||
for i := range blsPub {
|
||||
blsPub[i] = byte(i + 1)
|
||||
}
|
||||
var pop [bls.SignatureLen]byte
|
||||
for i := range pop {
|
||||
pop[i] = byte(0xFF - i)
|
||||
}
|
||||
|
||||
in := AddPermissionlessValidatorTxInput{
|
||||
NetworkID: 1337,
|
||||
BlockchainID: ids.ID{0xc1},
|
||||
Outs: sampleOuts(),
|
||||
Ins: sampleIns(),
|
||||
Credentials: sampleCreds(),
|
||||
Memo: []byte("apv"),
|
||||
|
||||
NodeID: ids.NodeID{0xaa, 0xbb, 0xcc},
|
||||
StakeStart: 1_700_000_000,
|
||||
StakeEnd: 1_800_000_000,
|
||||
StakeWeight: 1_000_000,
|
||||
StakeAssetID: ids.ID{0xaf},
|
||||
StakeAmount: 2_000_000_000,
|
||||
BLSPublicKey: blsPub,
|
||||
BLSProofOfPossession: pop,
|
||||
|
||||
ValidationRewardsOwner: OwnerStub{Threshold: 1, Locktime: 0, Address: ids.ShortID{0x10}},
|
||||
DelegationRewardsOwner: OwnerStub{Threshold: 1, Locktime: 0, Address: ids.ShortID{0x20}},
|
||||
DelegationShares: 100_000,
|
||||
}
|
||||
|
||||
tx := NewAddPermissionlessValidatorTx(in)
|
||||
if tx.NodeID() != in.NodeID {
|
||||
t.Errorf("NodeID round-trip")
|
||||
}
|
||||
if tx.StakeStart() != in.StakeStart {
|
||||
t.Errorf("StakeStart round-trip")
|
||||
}
|
||||
if tx.StakeEnd() != in.StakeEnd {
|
||||
t.Errorf("StakeEnd round-trip")
|
||||
}
|
||||
if tx.StakeWeight() != in.StakeWeight {
|
||||
t.Errorf("StakeWeight round-trip")
|
||||
}
|
||||
if tx.StakeAssetID() != in.StakeAssetID {
|
||||
t.Errorf("StakeAssetID round-trip")
|
||||
}
|
||||
if tx.StakeAmount() != in.StakeAmount {
|
||||
t.Errorf("StakeAmount round-trip")
|
||||
}
|
||||
if tx.BLSPublicKey() != blsPub {
|
||||
t.Errorf("BLSPublicKey round-trip")
|
||||
}
|
||||
if tx.BLSProofOfPossession() != pop {
|
||||
t.Errorf("BLSProofOfPossession round-trip")
|
||||
}
|
||||
vrThresh, vrLT, vrAddr := tx.ValidationRewardsOwner()
|
||||
if vrThresh != in.ValidationRewardsOwner.Threshold ||
|
||||
vrLT != in.ValidationRewardsOwner.Locktime ||
|
||||
vrAddr != in.ValidationRewardsOwner.Address {
|
||||
t.Errorf("ValidationRewardsOwner round-trip")
|
||||
}
|
||||
drThresh, drLT, drAddr := tx.DelegationRewardsOwner()
|
||||
if drThresh != in.DelegationRewardsOwner.Threshold ||
|
||||
drLT != in.DelegationRewardsOwner.Locktime ||
|
||||
drAddr != in.DelegationRewardsOwner.Address {
|
||||
t.Errorf("DelegationRewardsOwner round-trip")
|
||||
}
|
||||
if tx.DelegationShares() != in.DelegationShares {
|
||||
t.Errorf("DelegationShares round-trip")
|
||||
}
|
||||
|
||||
tx2, err := WrapAddPermissionlessValidatorTx(tx.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Wrap failed: %v", err)
|
||||
}
|
||||
if tx2.NodeID() != in.NodeID {
|
||||
t.Fatal("wrap-round-trip mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateChainTxRoundTrip(t *testing.T) {
|
||||
in := CreateChainTxInput{
|
||||
NetworkID: 1337,
|
||||
BlockchainID: ids.ID{0xc1},
|
||||
Outs: sampleOuts(),
|
||||
Ins: sampleIns(),
|
||||
Credentials: sampleCreds(),
|
||||
Memo: []byte("cc"),
|
||||
ParentNetwork: ids.ID{0xf1},
|
||||
VMID: ids.ID{0xf2},
|
||||
Owner: OwnerStub{Threshold: 1, Locktime: 1_900_000_000, Address: ids.ShortID{0x77}},
|
||||
WarpMessageHash: ids.ID{0xa1, 0xa2, 0xa3, 0xa4},
|
||||
GenesisData: []byte("genesis bytes for the new chain"),
|
||||
}
|
||||
|
||||
tx := NewCreateChainTx(in)
|
||||
if tx.NetworkID() != in.NetworkID {
|
||||
t.Errorf("NetworkID round-trip")
|
||||
}
|
||||
if tx.ParentNetwork() != in.ParentNetwork {
|
||||
t.Errorf("ParentNetwork round-trip")
|
||||
}
|
||||
if tx.VMID() != in.VMID {
|
||||
t.Errorf("VMID round-trip")
|
||||
}
|
||||
if tx.WarpMessageHash() != in.WarpMessageHash {
|
||||
t.Errorf("WarpMessageHash round-trip")
|
||||
}
|
||||
if !bytes.Equal(tx.GenesisData(), in.GenesisData) {
|
||||
t.Errorf("GenesisData round-trip")
|
||||
}
|
||||
ownerT, ownerLT, ownerAddr := tx.Owner()
|
||||
if ownerT != in.Owner.Threshold || ownerLT != in.Owner.Locktime || ownerAddr != in.Owner.Address {
|
||||
t.Errorf("Owner round-trip")
|
||||
}
|
||||
|
||||
tx2, err := WrapCreateChainTx(tx.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Wrap failed: %v", err)
|
||||
}
|
||||
if tx2.VMID() != in.VMID {
|
||||
t.Fatal("wrap-round-trip mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBatch3CrossConfusionStaysClosed verifies all 5 new tx types reject
|
||||
// every other type's bytes with ErrWrongTxKind, preserving the v3 TxKind
|
||||
// invariant across the expanded set.
|
||||
func TestBatch3CrossConfusionStaysClosed(t *testing.T) {
|
||||
baseFull := NewBaseTxFull(BaseTxFullInput{NetworkID: 1, BlockchainID: ids.ID{1}}).Bytes()
|
||||
importTx := NewImportTx(ImportTxInput{NetworkID: 1, BlockchainID: ids.ID{1}}).Bytes()
|
||||
exportTx := NewExportTx(ExportTxInput{NetworkID: 1, BlockchainID: ids.ID{1}}).Bytes()
|
||||
apv := NewAddPermissionlessValidatorTx(AddPermissionlessValidatorTxInput{
|
||||
NetworkID: 1, BlockchainID: ids.ID{1},
|
||||
}).Bytes()
|
||||
cc := NewCreateChainTx(CreateChainTxInput{NetworkID: 1, BlockchainID: ids.ID{1}}).Bytes()
|
||||
|
||||
// Pairwise: every WrapX(bufY) where Y != X must return ErrWrongTxKind.
|
||||
tests := []struct {
|
||||
name string
|
||||
fn func() error
|
||||
}{
|
||||
{"WrapBaseTxFull(importTx)", func() error { _, err := WrapBaseTxFull(importTx); return err }},
|
||||
{"WrapImportTx(baseFull)", func() error { _, err := WrapImportTx(baseFull); return err }},
|
||||
{"WrapExportTx(importTx)", func() error { _, err := WrapExportTx(importTx); return err }},
|
||||
{"WrapAddPermissionlessValidatorTx(cc)", func() error {
|
||||
_, err := WrapAddPermissionlessValidatorTx(cc)
|
||||
return err
|
||||
}},
|
||||
{"WrapCreateChainTx(apv)", func() error { _, err := WrapCreateChainTx(apv); return err }},
|
||||
{"WrapBaseTx(baseFull)", func() error { _, err := WrapBaseTx(baseFull); return err }},
|
||||
{"WrapAdvanceTimeTx(exportTx)", func() error { _, err := WrapAdvanceTimeTx(exportTx); return err }},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if err := tc.fn(); err != ErrWrongTxKind {
|
||||
t.Fatalf("%s: got %v, want ErrWrongTxKind", tc.name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package zap_native
|
||||
|
||||
import (
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
// CreateChainTx is the v3 create-blockchain envelope. It carries the
|
||||
// BaseTxFull spending state, the parent network ID, the child VM ID, an
|
||||
// optional genesis-data byte blob (variable; lives in the variable section),
|
||||
// a single-address Owner stub, and the SHA-256 hash of the corresponding
|
||||
// Warp message (when this CreateChain was originated via cross-network
|
||||
// commit). The Warp message body itself is NOT embedded — only its hash —
|
||||
// because the body lives in the signed Warp envelope outside this tx.
|
||||
//
|
||||
// Fixed-section layout (size 173 bytes):
|
||||
//
|
||||
// TxKind uint8 @ 0
|
||||
// NetworkID uint32 @ 1
|
||||
// BlockchainID 32B @ 5
|
||||
// OutsList 8B @ 37
|
||||
// InsList 8B @ 45
|
||||
// CredsList 8B @ 53
|
||||
// SigIndicesArr 8B @ 61
|
||||
// SigArr 8B @ 69
|
||||
// Memo 8B @ 77
|
||||
// ParentNetwork 32B @ 85 (ids.ID; the parent network)
|
||||
// VMID 32B @ 117 (ids.ID; the VM identifier)
|
||||
// OwnerThreshold uint32 @ 149
|
||||
// OwnerLocktime uint64 @ 153
|
||||
// OwnerAddress 20B @ 161
|
||||
// WarpMessageHash 32B @ 181 (sha256 of the originating Warp msg; zero if none)
|
||||
// GenesisData bytes @ 213 (offset+length; variable)
|
||||
// Size = 221
|
||||
const (
|
||||
OffsetCreateChainTx_NetworkID = 1
|
||||
OffsetCreateChainTx_BlockchainID = 5
|
||||
OffsetCreateChainTx_OutsList = 37
|
||||
OffsetCreateChainTx_InsList = 45
|
||||
OffsetCreateChainTx_CredsList = 53
|
||||
OffsetCreateChainTx_SigIndicesArr = 61
|
||||
OffsetCreateChainTx_SigArr = 69
|
||||
OffsetCreateChainTx_Memo = 77
|
||||
OffsetCreateChainTx_ParentNetwork = 85
|
||||
OffsetCreateChainTx_VMID = 117
|
||||
OffsetCreateChainTx_OwnerThreshold = 149
|
||||
OffsetCreateChainTx_OwnerLocktime = 153
|
||||
OffsetCreateChainTx_OwnerAddress = 161
|
||||
OffsetCreateChainTx_WarpMessageHash = OffsetCreateChainTx_OwnerAddress + ids.ShortIDLen
|
||||
OffsetCreateChainTx_GenesisData = OffsetCreateChainTx_WarpMessageHash + 32
|
||||
SizeCreateChainTx = OffsetCreateChainTx_GenesisData + 8
|
||||
)
|
||||
|
||||
// CreateChainTx is the zero-copy accessor.
|
||||
type CreateChainTx struct {
|
||||
msg *zap.Message
|
||||
obj zap.Object
|
||||
}
|
||||
|
||||
func (t CreateChainTx) NetworkID() uint32 { return t.obj.Uint32(OffsetCreateChainTx_NetworkID) }
|
||||
|
||||
func (t CreateChainTx) BlockchainID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetCreateChainTx_BlockchainID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t CreateChainTx) ParentNetwork() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetCreateChainTx_ParentNetwork + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t CreateChainTx) VMID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetCreateChainTx_VMID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t CreateChainTx) Owner() (uint32, uint64, ids.ShortID) {
|
||||
threshold := t.obj.Uint32(OffsetCreateChainTx_OwnerThreshold)
|
||||
locktime := t.obj.Uint64(OffsetCreateChainTx_OwnerLocktime)
|
||||
var addr ids.ShortID
|
||||
for i := 0; i < ids.ShortIDLen; i++ {
|
||||
addr[i] = t.obj.Uint8(OffsetCreateChainTx_OwnerAddress + i)
|
||||
}
|
||||
return threshold, locktime, addr
|
||||
}
|
||||
|
||||
// WarpMessageHash returns the SHA-256 hash of the originating Warp message.
|
||||
// Returns the zero ids.ID when this CreateChain was not originated via
|
||||
// a cross-network commit.
|
||||
func (t CreateChainTx) WarpMessageHash() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetCreateChainTx_WarpMessageHash + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// GenesisData returns the chain's genesis bytes.
|
||||
//
|
||||
// READ-ONLY: see BaseTxFull.Memo for the aliasing contract.
|
||||
func (t CreateChainTx) GenesisData() []byte {
|
||||
return t.obj.Bytes(OffsetCreateChainTx_GenesisData)
|
||||
}
|
||||
|
||||
func (t CreateChainTx) Outs() OutputList { return OutputListView(t.obj, OffsetCreateChainTx_OutsList) }
|
||||
func (t CreateChainTx) Ins() InputList { return InputListView(t.obj, OffsetCreateChainTx_InsList) }
|
||||
func (t CreateChainTx) Credentials() CredentialList {
|
||||
return CredentialListView(t.obj, OffsetCreateChainTx_CredsList)
|
||||
}
|
||||
func (t CreateChainTx) SigIndicesArray() SigIndicesArray {
|
||||
return SigIndicesArrayView(t.obj, OffsetCreateChainTx_SigIndicesArr)
|
||||
}
|
||||
func (t CreateChainTx) SignatureArray() SignatureArray {
|
||||
return SignatureArrayView(t.obj, OffsetCreateChainTx_SigArr)
|
||||
}
|
||||
|
||||
func (t CreateChainTx) Memo() []byte { return t.obj.Bytes(OffsetCreateChainTx_Memo) }
|
||||
|
||||
func (t CreateChainTx) Bytes() []byte { return t.msg.Bytes() }
|
||||
func (t CreateChainTx) IsZero() bool { return t.msg == nil }
|
||||
|
||||
func WrapCreateChainTx(b []byte) (CreateChainTx, error) {
|
||||
msg, err := zap.Parse(b)
|
||||
if err != nil {
|
||||
return CreateChainTx{}, err
|
||||
}
|
||||
obj := msg.Root()
|
||||
if TxKind(obj.Uint8(OffsetTxKind)) != TxKindCreateChain {
|
||||
return CreateChainTx{}, ErrWrongTxKind
|
||||
}
|
||||
return CreateChainTx{msg: msg, obj: obj}, nil
|
||||
}
|
||||
|
||||
// CreateChainTxInput is the constructor input.
|
||||
type CreateChainTxInput struct {
|
||||
NetworkID uint32
|
||||
BlockchainID ids.ID
|
||||
Outs []OutputListEntry
|
||||
Ins []InputListEntry
|
||||
Credentials []CredentialListEntry
|
||||
Memo []byte
|
||||
ParentNetwork ids.ID
|
||||
VMID ids.ID
|
||||
Owner OwnerStub
|
||||
WarpMessageHash ids.ID
|
||||
GenesisData []byte
|
||||
}
|
||||
|
||||
func NewCreateChainTx(in CreateChainTxInput) CreateChainTx {
|
||||
cap := zap.HeaderSize + 16 + SizeCreateChainTx
|
||||
cap += len(in.Outs) * SizeTransferableOutput
|
||||
cap += len(in.Ins) * SizeTransferableInput
|
||||
cap += len(in.Credentials) * SizeCredential
|
||||
cap += len(in.Memo) + len(in.GenesisData)
|
||||
b := zap.NewBuilder(cap)
|
||||
|
||||
outsOff, outsCount := WriteOutputList(b, in.Outs)
|
||||
insOff, insCount, sigIndices := WriteInputList(b, in.Ins)
|
||||
credsOff, credsCount, sigBlobs := WriteCredentialList(b, in.Credentials)
|
||||
sigIdxArrOff, sigIdxArrCount := WriteSigIndicesArray(b, sigIndices)
|
||||
sigArrOff, sigArrCount := WriteSignatureArray(b, sigBlobs)
|
||||
|
||||
ob := b.StartObject(SizeCreateChainTx)
|
||||
ob.SetUint8(OffsetTxKind, uint8(TxKindCreateChain))
|
||||
ob.SetUint32(OffsetCreateChainTx_NetworkID, in.NetworkID)
|
||||
for i := 0; i < 32; i++ {
|
||||
ob.SetUint8(OffsetCreateChainTx_BlockchainID+i, in.BlockchainID[i])
|
||||
ob.SetUint8(OffsetCreateChainTx_ParentNetwork+i, in.ParentNetwork[i])
|
||||
ob.SetUint8(OffsetCreateChainTx_VMID+i, in.VMID[i])
|
||||
ob.SetUint8(OffsetCreateChainTx_WarpMessageHash+i, in.WarpMessageHash[i])
|
||||
}
|
||||
ob.SetList(OffsetCreateChainTx_OutsList, outsOff, outsCount)
|
||||
ob.SetList(OffsetCreateChainTx_InsList, insOff, insCount)
|
||||
ob.SetList(OffsetCreateChainTx_CredsList, credsOff, credsCount)
|
||||
ob.SetList(OffsetCreateChainTx_SigIndicesArr, sigIdxArrOff, sigIdxArrCount)
|
||||
ob.SetList(OffsetCreateChainTx_SigArr, sigArrOff, sigArrCount)
|
||||
ob.SetBytes(OffsetCreateChainTx_Memo, in.Memo)
|
||||
ob.SetUint32(OffsetCreateChainTx_OwnerThreshold, in.Owner.Threshold)
|
||||
ob.SetUint64(OffsetCreateChainTx_OwnerLocktime, in.Owner.Locktime)
|
||||
for i := 0; i < ids.ShortIDLen; i++ {
|
||||
ob.SetUint8(OffsetCreateChainTx_OwnerAddress+i, in.Owner.Address[i])
|
||||
}
|
||||
ob.SetBytes(OffsetCreateChainTx_GenesisData, in.GenesisData)
|
||||
ob.FinishAsRoot()
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return CreateChainTx{msg: msg, obj: msg.Root()}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package zap_native
|
||||
|
||||
import (
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
// ExportTx is the v3 cross-chain export: send funds from this chain to a
|
||||
// destination chain. The Outs list carries BOTH local change outputs and the
|
||||
// exported outputs to the destination chain in a single fixed-stride array;
|
||||
// ExportedOutsStart / ExportedOutsCount mark the exported slice within.
|
||||
// Local Outs occupy positions [0, ExportedOutsStart); exported Outs occupy
|
||||
// [ExportedOutsStart, ExportedOutsStart+ExportedOutsCount).
|
||||
//
|
||||
// Fixed-section layout (size 125 bytes):
|
||||
//
|
||||
// TxKind uint8 @ 0
|
||||
// NetworkID uint32 @ 1
|
||||
// BlockchainID 32B @ 5
|
||||
// OutsList 8B @ 37 (combined: local change + exported)
|
||||
// InsList 8B @ 45 (local fee + funding inputs)
|
||||
// CredsList 8B @ 53
|
||||
// SigIndicesArr 8B @ 61
|
||||
// SigArr 8B @ 69
|
||||
// Memo 8B @ 77
|
||||
// DestinationNetwork 32B @ 85 (chain the exports flow to)
|
||||
// ExportedOutsStart uint32 @ 117 (index into Outs list)
|
||||
// ExportedOutsCount uint32 @ 121
|
||||
const (
|
||||
OffsetExportTx_NetworkID = 1
|
||||
OffsetExportTx_BlockchainID = 5
|
||||
OffsetExportTx_OutsList = 37
|
||||
OffsetExportTx_InsList = 45
|
||||
OffsetExportTx_CredsList = 53
|
||||
OffsetExportTx_SigIndicesArr = 61
|
||||
OffsetExportTx_SigArr = 69
|
||||
OffsetExportTx_Memo = 77
|
||||
OffsetExportTx_DestinationNetwork = 85
|
||||
OffsetExportTx_ExportedOutsStart = 117
|
||||
OffsetExportTx_ExportedOutsCount = 121
|
||||
SizeExportTx = 125
|
||||
)
|
||||
|
||||
// ExportTx is the zero-copy accessor for a v3 ExportTx.
|
||||
type ExportTx struct {
|
||||
msg *zap.Message
|
||||
obj zap.Object
|
||||
}
|
||||
|
||||
func (t ExportTx) NetworkID() uint32 { return t.obj.Uint32(OffsetExportTx_NetworkID) }
|
||||
|
||||
func (t ExportTx) BlockchainID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetExportTx_BlockchainID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DestinationNetwork returns the network identifier the exports flow to.
|
||||
func (t ExportTx) DestinationNetwork() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetExportTx_DestinationNetwork + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t ExportTx) Outs() OutputList { return OutputListView(t.obj, OffsetExportTx_OutsList) }
|
||||
func (t ExportTx) Ins() InputList { return InputListView(t.obj, OffsetExportTx_InsList) }
|
||||
func (t ExportTx) Credentials() CredentialList {
|
||||
return CredentialListView(t.obj, OffsetExportTx_CredsList)
|
||||
}
|
||||
func (t ExportTx) SigIndicesArray() SigIndicesArray {
|
||||
return SigIndicesArrayView(t.obj, OffsetExportTx_SigIndicesArr)
|
||||
}
|
||||
func (t ExportTx) SignatureArray() SignatureArray {
|
||||
return SignatureArrayView(t.obj, OffsetExportTx_SigArr)
|
||||
}
|
||||
|
||||
// ExportedOutsRange returns (start, count) marking the exported-output slice
|
||||
// within the Outs list. Outs[0..start) are local change;
|
||||
// Outs[start..start+count) are the exported outputs.
|
||||
func (t ExportTx) ExportedOutsRange() (uint32, uint32) {
|
||||
return t.obj.Uint32(OffsetExportTx_ExportedOutsStart),
|
||||
t.obj.Uint32(OffsetExportTx_ExportedOutsCount)
|
||||
}
|
||||
|
||||
// Memo returns the memo bytes. READ-ONLY (see BaseTxFull.Memo).
|
||||
func (t ExportTx) Memo() []byte { return t.obj.Bytes(OffsetExportTx_Memo) }
|
||||
|
||||
func (t ExportTx) Bytes() []byte { return t.msg.Bytes() }
|
||||
func (t ExportTx) IsZero() bool { return t.msg == nil }
|
||||
|
||||
func WrapExportTx(b []byte) (ExportTx, error) {
|
||||
msg, err := zap.Parse(b)
|
||||
if err != nil {
|
||||
return ExportTx{}, err
|
||||
}
|
||||
obj := msg.Root()
|
||||
if TxKind(obj.Uint8(OffsetTxKind)) != TxKindExport {
|
||||
return ExportTx{}, ErrWrongTxKind
|
||||
}
|
||||
return ExportTx{msg: msg, obj: obj}, nil
|
||||
}
|
||||
|
||||
// ExportTxInput is the constructor input for NewExportTx.
|
||||
type ExportTxInput struct {
|
||||
NetworkID uint32
|
||||
BlockchainID ids.ID
|
||||
DestinationNetwork ids.ID
|
||||
LocalOuts []OutputListEntry
|
||||
ExportedOuts []OutputListEntry
|
||||
Ins []InputListEntry
|
||||
Credentials []CredentialListEntry
|
||||
Memo []byte
|
||||
}
|
||||
|
||||
func NewExportTx(in ExportTxInput) ExportTx {
|
||||
cap := zap.HeaderSize + 16 + SizeExportTx
|
||||
cap += (len(in.LocalOuts) + len(in.ExportedOuts)) * SizeTransferableOutput
|
||||
cap += len(in.Ins) * SizeTransferableInput
|
||||
cap += len(in.Credentials) * SizeCredential
|
||||
cap += len(in.Memo)
|
||||
b := zap.NewBuilder(cap)
|
||||
|
||||
combinedOuts := make([]OutputListEntry, 0, len(in.LocalOuts)+len(in.ExportedOuts))
|
||||
combinedOuts = append(combinedOuts, in.LocalOuts...)
|
||||
combinedOuts = append(combinedOuts, in.ExportedOuts...)
|
||||
|
||||
outsOff, outsCount := WriteOutputList(b, combinedOuts)
|
||||
insOff, insCount, sigIndices := WriteInputList(b, in.Ins)
|
||||
credsOff, credsCount, sigBlobs := WriteCredentialList(b, in.Credentials)
|
||||
sigIdxArrOff, sigIdxArrCount := WriteSigIndicesArray(b, sigIndices)
|
||||
sigArrOff, sigArrCount := WriteSignatureArray(b, sigBlobs)
|
||||
|
||||
ob := b.StartObject(SizeExportTx)
|
||||
ob.SetUint8(OffsetTxKind, uint8(TxKindExport))
|
||||
ob.SetUint32(OffsetExportTx_NetworkID, in.NetworkID)
|
||||
for i := 0; i < 32; i++ {
|
||||
ob.SetUint8(OffsetExportTx_BlockchainID+i, in.BlockchainID[i])
|
||||
ob.SetUint8(OffsetExportTx_DestinationNetwork+i, in.DestinationNetwork[i])
|
||||
}
|
||||
ob.SetList(OffsetExportTx_OutsList, outsOff, outsCount)
|
||||
ob.SetList(OffsetExportTx_InsList, insOff, insCount)
|
||||
ob.SetList(OffsetExportTx_CredsList, credsOff, credsCount)
|
||||
ob.SetList(OffsetExportTx_SigIndicesArr, sigIdxArrOff, sigIdxArrCount)
|
||||
ob.SetList(OffsetExportTx_SigArr, sigArrOff, sigArrCount)
|
||||
ob.SetBytes(OffsetExportTx_Memo, in.Memo)
|
||||
ob.SetUint32(OffsetExportTx_ExportedOutsStart, uint32(len(in.LocalOuts)))
|
||||
ob.SetUint32(OffsetExportTx_ExportedOutsCount, uint32(len(in.ExportedOuts)))
|
||||
ob.FinishAsRoot()
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return ExportTx{msg: msg, obj: msg.Root()}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package zap_native
|
||||
|
||||
import (
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/zap"
|
||||
)
|
||||
|
||||
// ImportTx is the v3 cross-chain import: pull funds from a source chain via
|
||||
// imported UTXOs. The Ins list carries BOTH local fee-paying inputs and the
|
||||
// remote (imported) inputs in a single fixed-stride array; the
|
||||
// ImportedInsStart / ImportedInsCount header fields identify the imported
|
||||
// slice within. This keeps the SigIndicesArray / SignatureArray single
|
||||
// shared arrays across all inputs without re-base bookkeeping. Local Ins
|
||||
// occupy positions [0, ImportedInsStart); imported Ins occupy
|
||||
// [ImportedInsStart, ImportedInsStart+ImportedInsCount).
|
||||
//
|
||||
// Fixed-section layout (size 93 bytes):
|
||||
//
|
||||
// TxKind uint8 @ 0
|
||||
// NetworkID uint32 @ 1
|
||||
// BlockchainID 32B @ 5
|
||||
// OutsList 8B @ 37
|
||||
// InsList 8B @ 45 (combined: local + imported)
|
||||
// CredsList 8B @ 53
|
||||
// SigIndicesArr 8B @ 61
|
||||
// SigArr 8B @ 69
|
||||
// Memo 8B @ 77
|
||||
// SourceNetwork 32B @ 85 (...wait, that's 117. Re-do.)
|
||||
//
|
||||
// Actually wait. 85 + 32 = 117. Plus ImportedInsStart/Count (8) = 125.
|
||||
//
|
||||
// Re-layout to keep TxKind@0 alignment and end size 125 bytes:
|
||||
const (
|
||||
OffsetImportTx_NetworkID = 1
|
||||
OffsetImportTx_BlockchainID = 5
|
||||
OffsetImportTx_OutsList = 37
|
||||
OffsetImportTx_InsList = 45
|
||||
OffsetImportTx_CredsList = 53
|
||||
OffsetImportTx_SigIndicesArr = 61
|
||||
OffsetImportTx_SigArr = 69
|
||||
OffsetImportTx_Memo = 77
|
||||
OffsetImportTx_SourceNetwork = 85
|
||||
OffsetImportTx_ImportedInsStart = 117 // uint32 (index into Ins list)
|
||||
OffsetImportTx_ImportedInsCount = 121 // uint32
|
||||
SizeImportTx = 125
|
||||
)
|
||||
|
||||
// ImportTx is the zero-copy accessor for an v3 ImportTx.
|
||||
type ImportTx struct {
|
||||
msg *zap.Message
|
||||
obj zap.Object
|
||||
}
|
||||
|
||||
func (t ImportTx) NetworkID() uint32 { return t.obj.Uint32(OffsetImportTx_NetworkID) }
|
||||
|
||||
func (t ImportTx) BlockchainID() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetImportTx_BlockchainID + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// SourceNetwork returns the network identifier the UTXOs are imported from.
|
||||
func (t ImportTx) SourceNetwork() ids.ID {
|
||||
var out ids.ID
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = t.obj.Uint8(OffsetImportTx_SourceNetwork + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t ImportTx) Outs() OutputList { return OutputListView(t.obj, OffsetImportTx_OutsList) }
|
||||
func (t ImportTx) Ins() InputList { return InputListView(t.obj, OffsetImportTx_InsList) }
|
||||
func (t ImportTx) Credentials() CredentialList {
|
||||
return CredentialListView(t.obj, OffsetImportTx_CredsList)
|
||||
}
|
||||
func (t ImportTx) SigIndicesArray() SigIndicesArray {
|
||||
return SigIndicesArrayView(t.obj, OffsetImportTx_SigIndicesArr)
|
||||
}
|
||||
func (t ImportTx) SignatureArray() SignatureArray {
|
||||
return SignatureArrayView(t.obj, OffsetImportTx_SigArr)
|
||||
}
|
||||
|
||||
// ImportedInsRange returns (start, count) marking the imported-input slice
|
||||
// within the Ins list. Ins[0..start) are local fee-payers;
|
||||
// Ins[start..start+count) are the imported (remote-source) inputs.
|
||||
func (t ImportTx) ImportedInsRange() (uint32, uint32) {
|
||||
return t.obj.Uint32(OffsetImportTx_ImportedInsStart),
|
||||
t.obj.Uint32(OffsetImportTx_ImportedInsCount)
|
||||
}
|
||||
|
||||
// Memo returns the memo bytes. READ-ONLY (see BaseTxFull.Memo).
|
||||
func (t ImportTx) Memo() []byte { return t.obj.Bytes(OffsetImportTx_Memo) }
|
||||
|
||||
func (t ImportTx) Bytes() []byte { return t.msg.Bytes() }
|
||||
func (t ImportTx) IsZero() bool { return t.msg == nil }
|
||||
|
||||
func WrapImportTx(b []byte) (ImportTx, error) {
|
||||
msg, err := zap.Parse(b)
|
||||
if err != nil {
|
||||
return ImportTx{}, err
|
||||
}
|
||||
obj := msg.Root()
|
||||
if TxKind(obj.Uint8(OffsetTxKind)) != TxKindImport {
|
||||
return ImportTx{}, ErrWrongTxKind
|
||||
}
|
||||
return ImportTx{msg: msg, obj: obj}, nil
|
||||
}
|
||||
|
||||
// ImportTxInput is the constructor input for NewImportTx. LocalIns and
|
||||
// ImportedIns are written as a single combined list; the constructor
|
||||
// records ImportedInsStart = len(LocalIns).
|
||||
type ImportTxInput struct {
|
||||
NetworkID uint32
|
||||
BlockchainID ids.ID
|
||||
SourceNetwork ids.ID
|
||||
Outs []OutputListEntry
|
||||
LocalIns []InputListEntry
|
||||
ImportedIns []InputListEntry
|
||||
Credentials []CredentialListEntry
|
||||
Memo []byte
|
||||
}
|
||||
|
||||
func NewImportTx(in ImportTxInput) ImportTx {
|
||||
cap := zap.HeaderSize + 16 + SizeImportTx
|
||||
cap += len(in.Outs) * SizeTransferableOutput
|
||||
cap += (len(in.LocalIns) + len(in.ImportedIns)) * SizeTransferableInput
|
||||
cap += len(in.Credentials) * SizeCredential
|
||||
cap += len(in.Memo)
|
||||
b := zap.NewBuilder(cap)
|
||||
|
||||
combinedIns := make([]InputListEntry, 0, len(in.LocalIns)+len(in.ImportedIns))
|
||||
combinedIns = append(combinedIns, in.LocalIns...)
|
||||
combinedIns = append(combinedIns, in.ImportedIns...)
|
||||
|
||||
outsOff, outsCount := WriteOutputList(b, in.Outs)
|
||||
insOff, insCount, sigIndices := WriteInputList(b, combinedIns)
|
||||
credsOff, credsCount, sigBlobs := WriteCredentialList(b, in.Credentials)
|
||||
sigIdxArrOff, sigIdxArrCount := WriteSigIndicesArray(b, sigIndices)
|
||||
sigArrOff, sigArrCount := WriteSignatureArray(b, sigBlobs)
|
||||
|
||||
ob := b.StartObject(SizeImportTx)
|
||||
ob.SetUint8(OffsetTxKind, uint8(TxKindImport))
|
||||
ob.SetUint32(OffsetImportTx_NetworkID, in.NetworkID)
|
||||
for i := 0; i < 32; i++ {
|
||||
ob.SetUint8(OffsetImportTx_BlockchainID+i, in.BlockchainID[i])
|
||||
ob.SetUint8(OffsetImportTx_SourceNetwork+i, in.SourceNetwork[i])
|
||||
}
|
||||
ob.SetList(OffsetImportTx_OutsList, outsOff, outsCount)
|
||||
ob.SetList(OffsetImportTx_InsList, insOff, insCount)
|
||||
ob.SetList(OffsetImportTx_CredsList, credsOff, credsCount)
|
||||
ob.SetList(OffsetImportTx_SigIndicesArr, sigIdxArrOff, sigIdxArrCount)
|
||||
ob.SetList(OffsetImportTx_SigArr, sigArrOff, sigArrCount)
|
||||
ob.SetBytes(OffsetImportTx_Memo, in.Memo)
|
||||
ob.SetUint32(OffsetImportTx_ImportedInsStart, uint32(len(in.LocalIns)))
|
||||
ob.SetUint32(OffsetImportTx_ImportedInsCount, uint32(len(in.ImportedIns)))
|
||||
ob.FinishAsRoot()
|
||||
msg, _ := zap.Parse(b.Finish())
|
||||
return ImportTx{msg: msg, obj: msg.Root()}
|
||||
}
|
||||
@@ -18,17 +18,23 @@ import "errors"
|
||||
type TxKind uint8
|
||||
|
||||
const (
|
||||
TxKindReserved TxKind = 0
|
||||
TxKindAdvanceTime TxKind = 1
|
||||
TxKindRewardValidator TxKind = 2
|
||||
TxKindSetL1ValidatorWeight TxKind = 3
|
||||
TxKindIncreaseL1ValidatorBalance TxKind = 4
|
||||
TxKindDisableL1Validator TxKind = 5
|
||||
TxKindBase TxKind = 6
|
||||
TxKindRegisterL1Validator TxKind = 7
|
||||
TxKindSlashValidator TxKind = 8
|
||||
TxKindTransferChainOwnership TxKind = 9
|
||||
TxKindRemoveChainValidator TxKind = 10
|
||||
TxKindReserved TxKind = 0
|
||||
TxKindAdvanceTime TxKind = 1
|
||||
TxKindRewardValidator TxKind = 2
|
||||
TxKindSetL1ValidatorWeight TxKind = 3
|
||||
TxKindIncreaseL1ValidatorBalance TxKind = 4
|
||||
TxKindDisableL1Validator TxKind = 5
|
||||
TxKindBase TxKind = 6
|
||||
TxKindRegisterL1Validator TxKind = 7
|
||||
TxKindSlashValidator TxKind = 8
|
||||
TxKindTransferChainOwnership TxKind = 9
|
||||
TxKindRemoveChainValidator TxKind = 10
|
||||
// Batch 3 — tx types that compose batch-3 list/object primitives:
|
||||
TxKindBaseFull TxKind = 11 // BaseTx with Outs+Ins+Credentials
|
||||
TxKindAddPermissionlessValidator TxKind = 12
|
||||
TxKindImport TxKind = 13
|
||||
TxKindExport TxKind = 14
|
||||
TxKindCreateChain TxKind = 15
|
||||
)
|
||||
|
||||
// OffsetTxKind is the fixed wire position of the discriminator. Every
|
||||
|
||||
Reference in New Issue
Block a user