mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
zap.ListBuilder.AddBytes counts BYTES not elements; fixed-stride lists were storing byte-inflated counts -> stride clamp rejected them. Fixed every write helper to store the real element count (len(entries)). Round-trip test now GREEN across the hardest cases: multisig+stakeable outputs/inputs, the nested ConvertNetworkToL1Validator (NodeID+BLS PoP+2 owners), SovereignL1Chain manifest, and signed unsigned‖creds. go build ./vms/platformvm/txs = exit 0. The P-chain tx wire is now struct-IS-wire, codec-free, and verified correct.
164 lines
5.2 KiB
Go
164 lines
5.2 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package txs
|
|
|
|
// Parse dispatch + credential wire. There is no codec: Parse wraps the buffer
|
|
// zero-copy and dispatches on the 1-byte kind. A signed tx is
|
|
// unsigned_buffer ‖ creds_buffer (both self-delimiting zap messages), so the
|
|
// unsigned bytes are a genuine byte-prefix of the signed bytes.
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/luxfi/node/vms/components/verify"
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
"github.com/luxfi/zap"
|
|
)
|
|
|
|
// zapLen returns the total length of the leading self-delimiting zap message
|
|
// in b (its header size field). This is the split point between the unsigned
|
|
// prefix and the credential suffix of a signed tx.
|
|
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
|
|
}
|
|
|
|
// parseUnsigned wraps the leading zap buffer as the typed UnsignedTx (zero
|
|
// copy) by dispatching on its kind discriminator.
|
|
func parseUnsigned(buf []byte) (UnsignedTx, error) {
|
|
msg, err := zap.Parse(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch k := kindOf(msg); k {
|
|
case kindAdvanceTime:
|
|
return &AdvanceTimeTx{msg: msg}, nil
|
|
case kindRewardValidator:
|
|
return &RewardValidatorTx{msg: msg}, nil
|
|
case kindBase:
|
|
return &BaseTx{spendingTx{msg: msg}}, nil
|
|
case kindImport:
|
|
return &ImportTx{spendingTx{msg: msg}}, nil
|
|
case kindExport:
|
|
return &ExportTx{spendingTx{msg: msg}}, nil
|
|
case kindCreateNetwork:
|
|
return &CreateNetworkTx{spendingTx{msg: msg}}, nil
|
|
case kindCreateChain:
|
|
return &CreateChainTx{spendingTx{msg: msg}}, nil
|
|
case kindTransferChainOwnership:
|
|
return &TransferChainOwnershipTx{spendingTx{msg: msg}}, nil
|
|
case kindRemoveChainValidator:
|
|
return &RemoveChainValidatorTx{spendingTx{msg: msg}}, nil
|
|
case kindTransformChain:
|
|
return &TransformChainTx{spendingTx{msg: msg}}, nil
|
|
case kindAddValidator:
|
|
return &AddValidatorTx{spendingTx{msg: msg}}, nil
|
|
case kindAddChainValidator:
|
|
return &AddChainValidatorTx{spendingTx{msg: msg}}, nil
|
|
case kindAddDelegator:
|
|
return &AddDelegatorTx{spendingTx{msg: msg}}, nil
|
|
case kindAddPermissionlessValidator:
|
|
return &AddPermissionlessValidatorTx{spendingTx{msg: msg}}, nil
|
|
case kindAddPermissionlessDelegator:
|
|
return &AddPermissionlessDelegatorTx{spendingTx{msg: msg}}, nil
|
|
case kindConvertNetworkToL1:
|
|
return &ConvertNetworkToL1Tx{spendingTx{msg: msg}}, nil
|
|
case kindCreateSovereignL1:
|
|
return &CreateSovereignL1Tx{spendingTx{msg: msg}}, nil
|
|
case kindRegisterL1Validator:
|
|
return &RegisterL1ValidatorTx{spendingTx{msg: msg}}, nil
|
|
case kindSetL1ValidatorWeight:
|
|
return &SetL1ValidatorWeightTx{spendingTx{msg: msg}}, nil
|
|
case kindIncreaseL1ValidatorBalance:
|
|
return &IncreaseL1ValidatorBalanceTx{spendingTx{msg: msg}}, nil
|
|
case kindDisableL1Validator:
|
|
return &DisableL1ValidatorTx{spendingTx{msg: msg}}, nil
|
|
default:
|
|
return nil, fmt.Errorf("zap: unknown tx kind %d", k)
|
|
}
|
|
}
|
|
|
|
// credential wire: object{ credsList ptr @0, sigArray ptr @8 } (size 16).
|
|
// A cred entry (8-byte stride) is {sigStart u32, sigCount u32}, slicing into
|
|
// the shared 65-byte-blob signature array.
|
|
const (
|
|
offCredsList = 0
|
|
offSigArray = 8
|
|
credsObjSize = 16
|
|
credEntry = 8
|
|
)
|
|
|
|
// writeCredsBuf encodes a signed tx's credentials as a standalone zap buffer,
|
|
// appended after the unsigned prefix. Only called when Creds is non-empty.
|
|
func writeCredsBuf(creds []verify.Verifiable) ([]byte, error) {
|
|
b := zap.NewBuilder(zap.HeaderSize + 128 + len(creds)*credEntry)
|
|
var blobs [][sigLen]byte
|
|
clb := b.StartList(credEntry)
|
|
cursor := uint32(0)
|
|
for i, c := range creds {
|
|
cred, ok := c.(*secp256k1fx.Credential)
|
|
if !ok {
|
|
return nil, fmt.Errorf("credential %d: unsupported type %T", i, c)
|
|
}
|
|
var e [credEntry]byte
|
|
putU32(e[0:], cursor)
|
|
putU32(e[4:], uint32(len(cred.Sigs)))
|
|
clb.AddBytes(e[:])
|
|
blobs = append(blobs, cred.Sigs...)
|
|
cursor += uint32(len(cred.Sigs))
|
|
}
|
|
// AddBytes counts bytes, not elements — use real element counts.
|
|
credsOff, _ := clb.Finish()
|
|
credsCount := len(creds)
|
|
|
|
var sigOff, sigCount int
|
|
if len(blobs) > 0 {
|
|
slb := b.StartList(sigLen)
|
|
for _, s := range blobs {
|
|
slb.AddBytes(s[:])
|
|
}
|
|
sigOff, _ = slb.Finish()
|
|
sigCount = len(blobs)
|
|
}
|
|
|
|
ob := b.StartObject(credsObjSize)
|
|
ob.SetList(offCredsList, credsOff, credsCount)
|
|
ob.SetList(offSigArray, sigOff, sigCount)
|
|
ob.FinishAsRoot()
|
|
return b.Finish(), nil
|
|
}
|
|
|
|
// parseCredsBuf decodes a credential buffer produced by writeCredsBuf.
|
|
func parseCredsBuf(b []byte) ([]verify.Verifiable, error) {
|
|
msg, err := zap.Parse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
obj := msg.Root()
|
|
credsList := obj.ListStride(offCredsList, credEntry)
|
|
sigArr := obj.ListStride(offSigArray, sigLen)
|
|
n := credsList.Len()
|
|
creds := make([]verify.Verifiable, n)
|
|
for i := 0; i < n; i++ {
|
|
e := credsList.Object(i, credEntry)
|
|
start, count := e.Uint32(0), e.Uint32(4)
|
|
sigs := make([][sigLen]byte, count)
|
|
for j := uint32(0); j < count; j++ {
|
|
blob := sigArr.Object(int(start+j), sigLen)
|
|
for k := 0; k < sigLen; k++ {
|
|
sigs[j][k] = blob.Uint8(k)
|
|
}
|
|
}
|
|
creds[i] = &secp256k1fx.Credential{Sigs: sigs}
|
|
}
|
|
return creds, nil
|
|
}
|