Files
zeekay f480e48e39 run on the native fleet
Adds .hanzo/workflows/cicd.yml calling the canonical pipeline and drops the
GitHub-hosted workflows it replaces. Lands here because this is where the repo
is written — git.hanzo.ai is upstream and github.com is its push mirror, so the
same change made on github.com never reaches the runner fleet.
2026-08-20 20:20:25 -07:00

131 lines
4.1 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package txs
// Credential wire + the split point of a signed tx. A signed tx is
// unsigned_buffer ‖ creds_buffer, both self-delimiting zap buffers, so the
// unsigned bytes are a genuine byte-prefix of the signed bytes and the
// unsigned buffer's own size field is where a reader splits them.
import (
"encoding/binary"
"errors"
"fmt"
"github.com/luxfi/utxo/secp256k1fx"
"github.com/luxfi/vm/components/verify"
"github.com/luxfi/zap"
)
// errCredSigsOutOfRange is returned when a credential entry names a signature
// range the buffer's signature array does not contain.
var errCredSigsOutOfRange = errors.New("txs: credential signature range is outside the signature array")
// 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
}
// 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()
total := uint32(sigArr.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)
// The claimed range must lie inside the signature array; both bounds
// come off the wire. start <= total makes total-start safe.
if start > total || count > total-start {
return nil, fmt.Errorf("%w: credential %d claims signatures [%d, %d) of %d",
errCredSigsOutOfRange, i, start, uint64(start)+uint64(count), total)
}
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
}
// concat returns a ‖ b as a fresh slice. Never append into the unsigned
// buffer: its backing array is the zap builder's and its size field at
// [12:16] must keep naming the unsigned length.
func concat(a, b []byte) []byte {
out := make([]byte, 0, len(a)+len(b))
out = append(out, a...)
out = append(out, b...)
return out
}