arkscript

package
v0.1.1-rc2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ARKNUMSHex is the hex encoded version of the ARK NUMs key.
	ARKNUMSHex = "02372f225b3caee8213096de3229ee4335306b0" +
		"7c3c169438461b5d4749884ec65"

	// ARKNUMSSeedPhrase is the seed phrase used to generate
	// the ARK NUMS key.
	ARKNUMSSeedPhrase = "Ark Protocol NUMS"
)
View Source
const (

	// MaxPolicyTemplateBytes caps the raw size of a policy template
	// blob accepted for decoding. Standard Ark policies are well under
	// 1 KiB; the headroom tolerates future custom policies while still
	// preventing decode-bomb amplification from untrusted sources.
	MaxPolicyTemplateBytes = 64 * 1024

	// MaxLeafTemplateBytes caps the raw size of a single leaf template
	// blob. Sized to hold the largest legitimate vHTLC-style leaf with
	// comfortable headroom.
	MaxLeafTemplateBytes = 16 * 1024

	// MaxPolicyLeaves caps the number of leaves in a single policy
	// template. Standard Ark VTXO policies have 2 leaves; vHTLC has
	// 6. The cap is intentionally generous for future policy shapes
	// while still bounding the per-policy work.
	MaxPolicyLeaves = 32

	// MaxPolicyDepth caps the AST recursion depth during decode. Ark
	// policies nest at most a couple of levels (e.g. CSV->Multisig,
	// Condition->Multisig). Anything deeper is either a bug or an
	// attempt to cause stack/CPU blowup.
	MaxPolicyDepth = 16

	// MaxPolicyNodes caps the total node count decoded from a single
	// policy or leaf blob. Prevents an attacker from amplifying a
	// moderate-size blob into a huge in-memory AST.
	MaxPolicyNodes = 256

	// MaxMultisigKeys caps the number of keys inside a single Multisig
	// node. Ark multisig nodes typically have 1-3 keys.
	MaxMultisigKeys = 64
)
View Source
const (
	// CollabMultisigLeafWitnessSize is the estimated witness size for
	// the collaborative spend path of a boarding or vtxo output.
	CollabMultisigLeafWitnessSize = lntypes.WeightUnit(1 + 64 + 1 + 64)

	// UnilateralTimeoutLeafWitnessSize is the estimated witness size
	// for the unilateral timeout path of a boarding or vtxo output.
	UnilateralTimeoutLeafWitnessSize = lntypes.WeightUnit(1 + 64)
)
View Source
const PSBTKeyConditionWitness = PSBTKeyPrefix + "condition"

PSBTKeyConditionWitness is the PSBT key for Ark condition witness metadata.

View Source
const PSBTKeyPrefix = "ark/"

PSBTKeyPrefix is the namespace prefix for Ark PSBT keys.

View Source
const PSBTKeyTapTree = PSBTKeyPrefix + "taptree"

PSBTKeyTapTree is the PSBT key for tap tree encoding.

Variables

View Source
var (
	// ErrMissingCollab indicates no collab leaf was found.
	ErrMissingCollab = &VTXOValidationError{
		Code:    "MISSING_COLLAB",
		Message: "VTXO policy must contain a collab leaf",
	}

	// ErrMissingExit indicates no exit leaf was found.
	ErrMissingExit = &VTXOValidationError{
		Code:    "MISSING_EXIT",
		Message: "VTXO policy must contain an exit leaf",
	}

	// ErrExitNotCSVGated indicates the exit leaf is not CSV-gated.
	ErrExitNotCSVGated = &VTXOValidationError{
		Code:    "EXIT_NOT_CSV_GATED",
		Message: "exit leaf must be CSV-gated",
	}
)

Standard validation error codes.

View Source
var (
	// ARKNUMSKey is a NUMS key (nothing up my sleeves number) that has
	// no known private key. This was generated using the following script:
	// https://github.com/lightninglabs/lightning-node-connect/tree/
	// master/mailbox/numsgen, with the seed phrase "Ark Protocol NUMS".
	ARKNUMSKey = mustParsePubKey(ARKNUMSHex)
)
View Source
var (
	// AnchorPkScript is a standard P2A script. This is a keyless output.
	// This corresponds to address: bc1pfeessrawgf.
	// The script path for this output requires no witness elements,
	// meaning that it can be spent by anyone who provides the script and
	// control block.
	AnchorPkScript = []byte{
		txscript.OP_TRUE,
		txscript.OP_DATA_2,

		0x4e, 0x73,
	}
)
View Source
var (
	// ErrConditionWitnessNotFound indicates that a PSBT input does not
	// include Ark condition witness metadata.
	ErrConditionWitnessNotFound = errors.New("condition witness not found")
)

Functions

func AbsoluteLockTimeCondition

func AbsoluteLockTimeCondition(lock uint32) ([]byte, error)

AbsoluteLockTimeCondition builds the canonical script prefix for nLockTime >= lock enforced via OP_CHECKLOCKTIMEVERIFY.

func AnchorOutput

func AnchorOutput(opts ...AnchorOption) *wire.TxOut

AnchorOutput returns a P2A output. Without options the value is zero, matching the BIP-433 ephemeral-anchor pattern used by canonical Ark tree / forfeit / checkpoint parents that pay 0 fee themselves and are confirmed via a CPFP child. Callers whose parent pays a non-zero fee must pass WithAnchorValue to lift the anchor above dust (the P2A dust threshold is 240 sats by default), so bitcoind does not reject the parent with "dust, tx with dust output must be 0-fee". The returned PkScript is a defensive copy so callers cannot mutate the package-level global.

func CheckpointPkScript

func CheckpointPkScript(policy CheckpointPolicy,
	ownerLeafScript []byte) ([]byte, error)

CheckpointPkScript returns the pkScript for a checkpoint output produced by CheckpointTapScript.

func CheckpointTapScript

func CheckpointTapScript(policy CheckpointPolicy,
	ownerLeafScript []byte) (*waddrmgr.Tapscript, error)

CheckpointTapScript constructs the tapscript for an OOR checkpoint output.

The checkpoint tree for v0 is a simple two-leaf tree:

  • an operator-controlled CSV unroll leaf (operator key + relative timelock),
  • a collaborative leaf between operator and VTXO owner (provided by the caller as raw script bytes).

For v0, the checkpoint output always uses the ARK NUMS internal key so there is no key-path spend and all spends go through one of the script leaves.

func ContainsKey

func ContainsKey(node Node, key *btcec.PublicKey) bool

ContainsKey walks the typed AST nodes (Multisig, CSV, Condition) and returns true if any Multisig node references the given public key. Opaque predicate bytes are intentionally not inspected.

func DecodeConditionWitness

func DecodeConditionWitness(data []byte) ([][]byte, error)

DecodeConditionWitness deserializes Ark condition witness items from PSBT storage.

func DeriveLockTime

func DeriveLockTime(node Node) uint32

DeriveLockTime returns the required nLockTime value for spending the given AST node. This implements the tx-context derivation rules:

  • If the node contains a CLTV condition, return the locktime value.
  • Otherwise return 0.

func DeriveSequence

func DeriveSequence(node Node) uint32

DeriveSequence returns the required nSequence value for spending the given AST node. This implements the tx-context derivation rules from the RFC:

  • If the node contains CSV(lock, ...), return the lock value.
  • If the node contains CLTV, return the "non-final" sentinel (wire.MaxTxInSequenceNum - 1) so OP_CHECKLOCKTIMEVERIFY can evaluate.
  • Otherwise return the fully-final wire.MaxTxInSequenceNum.

func EncodeConditionWitness

func EncodeConditionWitness(items [][]byte) ([]byte, error)

EncodeConditionWitness serializes Ark condition witness items for PSBT storage. The format is a standard Bitcoin witness vector: <count><item0><item1>..., with each item encoded as varbytes.

func EncodeNode

func EncodeNode(node Node) ([]byte, error)

EncodeNode serializes one semantic AST node into a stable binary encoding.

func EncodeStandardVTXOArtifacts

func EncodeStandardVTXOArtifacts(ownerKey, operatorKey *btcec.PublicKey,
	exitDelay uint32) ([]byte, []byte, error)

EncodeStandardVTXOArtifacts is a convenience that returns both the encoded policy template bytes and the canonical P2TR pkScript for the standard Ark VTXO shape defined by (ownerKey, operatorKey, exitDelay). Callers that need the full tree-construction descriptor should use tree.NewVTXODescriptor instead — this helper is for surfaces that only need the output-level artifacts (e.g. recipient descriptor construction in the wallet, where the ephemeral MuSig2 signing key is derived later by the round FSM).

func EncodeStandardVTXOTemplate

func EncodeStandardVTXOTemplate(ownerKey, operatorKey *btcec.PublicKey,
	exitDelay uint32) ([]byte, error)

EncodeStandardVTXOTemplate serializes the standard Ark VTXO policy.

func EncodeTapTree

func EncodeTapTree(policy *CompiledPolicy) ([]byte, error)

EncodeTapTree serializes a compiled policy's leaves into an Ark-specific tap tree encoding. This is NOT the BIP-371 PSBT_OUT_TAP_TREE format (which uses depth-first tuples without a leading count). The format is: - leaf count (compact size uint) - for each leaf:

  • depth (1 byte)
  • leaf version (1 byte)
  • script length (compact size uint)
  • script bytes

Leaves are encoded in canonical order.

func ExtractAbsoluteLockTime

func ExtractAbsoluteLockTime(node Node) uint32

ExtractAbsoluteLockTime returns the absolute locktime required by the AST node, or zero when no recognized CLTV predicate is present.

func ExtractCSVDelay

func ExtractCSVDelay(node Node) uint32

ExtractCSVDelay returns the CSV lock value if the AST contains a CSV node, or 0 if it does not. If multiple CSV nodes are nested, the outermost lock is returned.

func GetConditionWitnessPSBTInput

func GetConditionWitnessPSBTInput(input psbt.PInput) ([][]byte, error)

GetConditionWitnessPSBTInput retrieves Ark condition witness items stored in the given PSBT input's unknown fields using PSBTKeyConditionWitness.

func Hash160Condition

func Hash160Condition(hash []byte) ([]byte, error)

Hash160Condition builds the canonical script prefix for HASH160(<witness_item>) == hash.

func IsStandardVTXOTemplate

func IsStandardVTXOTemplate(template *PolicyTemplate) bool

IsStandardVTXOTemplate returns true when the policy matches the standard Ark VTXO shape.

func MaybeAppendSighash

func MaybeAppendSighash(sig input.Signature,
	sigHash txscript.SigHashType) []byte

MaybeAppendSighash appends a sighash type to the end of a signature if the sighash type isn't sighash default.

func MultiSigCollabTapLeaf

func MultiSigCollabTapLeaf(ownerKey, cosignerKey *btcec.PublicKey) (
	txscript.TapLeaf, error)

MultiSigCollabTapLeaf returns the full tapscript leaf for the collaborative multisig script spend path between owner and cosigner.

The canonical script encoding matches the arkscript Multisig node:

<owner_key_xonly> OP_CHECKSIGVERIFY <cosigner_key_xonly> OP_CHECKSIG

func PaymentHash160Condition

func PaymentHash160Condition(paymentHash lntypes.Hash) ([]byte, error)

PaymentHash160Condition builds the canonical vHTLC success predicate script for a 32-byte Lightning payment preimage: OP_SIZE 32 EQUALVERIFY HASH160 <HASH160(payment_hash)> EQUALVERIFY.

func PolicyRoot

func PolicyRoot(policy *CompiledPolicy) chainhash.Hash

PolicyRoot returns just the policy root hash from a compiled policy. This can be used when the caller needs to provide the root for external composition. The policy must not be nil.

func PutConditionWitnessPSBTInput

func PutConditionWitnessPSBTInput(pkt *psbt.Packet, inputIndex int,
	items [][]byte) error

PutConditionWitnessPSBTInput stores the given Ark condition witness items into the specified PSBT input's unknown fields using PSBTKeyConditionWitness.

func ScriptContainsKey

func ScriptContainsKey(script []byte, key *btcec.PublicKey) bool

ScriptContainsKey performs a byte-level scan of a compiled script for the x-only serialized public key. This is a lightweight server-side heuristic that doesn't require AST parsing.

The check verifies that the 32-byte x-only key appears somewhere in the raw script bytes. This can produce false positives if the key bytes happen to span an opcode boundary, but combined with full script VM execution at finalize time the result is safe: the heuristic gates admission, the VM proves correctness.

func SignVTXOCollabInput

func SignVTXOCollabInput(signer input.Signer, tx *wire.MsgTx, inputIndex int,
	spendInfo *SpendInfo, keyDesc *keychain.KeyDescriptor,
	output *wire.TxOut, sigHashes *txscript.TxSigHashes,
	prevFetcher txscript.PrevOutputFetcher) (input.Signature, error)

SignVTXOCollabInput signs the collaborative multisig input of a VTXO output.

func SigningKeys

func SigningKeys(node Node) ([]*btcec.PublicKey, error)

SigningKeys returns the tapscript CHECKSIG public keys committed to by node in witness-stack order. Today Ark policy leaves are built from a single Multisig, optionally wrapped by CSV/Condition gates. Returning the concrete script order lets callers assemble or validate witnesses without parsing compiled script bytes.

func SigningKeysForSpendPath

func SigningKeysForSpendPath(template *PolicyTemplate,
	spendPath *SpendPath) ([]*btcec.PublicKey, error)

SigningKeysForSpendPath locates the semantic template leaf selected by spendPath and returns the leaf's required signing keys in witness-stack order.

func UnilateralCSVTimeoutTapLeaf

func UnilateralCSVTimeoutTapLeaf(timeoutKey *btcec.PublicKey,
	csvDelay uint32) (txscript.TapLeaf, error)

UnilateralCSVTimeoutTapLeaf constructs the tap leaf used as the timeout path for boarding or VTXO outputs.

The canonical script encoding matches the arkscript CSV node:

<timeout_key_xonly> OP_CHECKSIG <exit_delay> OP_CSV OP_DROP

The csvDelay parameter is a raw block count; it is converted to the BIP-68 block-mode sequence encoding before being stored on the leaf.

func VTXOTapKey

func VTXOTapKey(ownerKey, cosignerKey *btcec.PublicKey, exitDelay uint32) (
	*btcec.PublicKey, error)

VTXOTapKey computes the taproot output key for a standard VTXO tapscript.

func VTXOTapScript

func VTXOTapScript(ownerKey, cosignerKey *btcec.PublicKey, exitDelay uint32) (
	*waddrmgr.Tapscript, error)

VTXOTapScript constructs the full tapscript for a VTXO type output. This output structure is used for both boarding UTXOs as well as VTXOs. The tree consists of:

  • an unspendable NUMS keypath.
  • Collaborative spend path between owner and cosigner.
  • Timeout path allowing the owner to recover funds after a CSV exit delay.

func VTXOTimeoutSpendWitness

func VTXOTimeoutSpendWitness(signer input.Signer,
	signDesc *input.SignDescriptor,
	sweepTx *wire.MsgTx) (wire.TxWitness, error)

VTXOTimeoutSpendWitness constructs the witness stack needed to spend the timeout path of a VTXO output.

func ValidatePolicy

func ValidatePolicy(nodes []Node, opts PolicyValidationOpts) error

ValidatePolicy checks that a set of AST nodes satisfies the structural Ark policy invariants inferred from the signer structure:

  1. At least one operator-containing leaf (collab).
  2. At least one non-operator leaf (exit).
  3. No leaf permits the operator to spend unilaterally (every Multisig node that contains the operator key must contain at least one other key, so the operator always needs a cooperating signer).
  4. Every non-operator leaf is CSV-gated.
  5. When opts.MinExitDelay > 0, the smallest inferred exit delay must be at least opts.MinExitDelay. When opts.MinExitDelay == 0, this check is skipped — use ValidateStandardVTXOPolicy to require a non-zero minimum.

ValidatePolicy is the correct admission check for custom policy shapes (e.g. vHTLC) whose unilateral delays are protocol-specific rather than tied to the operator's standard VTXO exit delay.

func ValidateStandardVTXOPolicy

func ValidateStandardVTXOPolicy(nodes []Node, operatorKey *btcec.PublicKey,
	minExitDelay uint32) error

ValidateStandardVTXOPolicy is the admission check for a standard Ark VTXO recipient. It enforces every invariant of ValidatePolicy and additionally requires a non-zero MinExitDelay. A zero minimum is rejected fail-closed because it would otherwise silently accept a policy with a 1-block CSV, breaking the forfeit incentive.

Types

type AnchorOption

type AnchorOption func(*wire.TxOut)

AnchorOption customizes the P2A output returned by AnchorOutput. The default (no options) produces a zero-value ephemeral anchor suitable for BIP-431 TRUC parents that pay zero fee themselves and rely on a CPFP descendant to fund the package (per the BIP-433 ephemeral-dust rule). Callers whose parent tx pays its own miner fee must use WithAnchorValue to lift the anchor above the P2A dust threshold; otherwise relay rejects the parent with "dust, tx with dust output must be 0-fee".

func WithAnchorValue

func WithAnchorValue(sats int64) AnchorOption

WithAnchorValue overrides the default zero-value anchor with a caller- supplied sat amount. Per BIP-433 the P2A dust threshold is 240 sats, so any value strictly greater than 240 takes the output out of the ephemeral-dust regime and lets the parent pay a non-zero fee.

type CSV

type CSV struct {
	// Lock is the BIP-68 encoded relative locktime value.
	Lock uint32

	// Inner is the expression to gate with the timelock.
	Inner Node
}

CSV represents a relative timelock gate using OP_CHECKSEQUENCEVERIFY. The inner expression is evaluated first, then the CSV check is performed. Canonical encoding: <inner> <lock> OP_CHECKSEQUENCEVERIFY OP_DROP.

func (*CSV) Script

func (c *CSV) Script() ([]byte, error)

Script compiles the CSV node to its canonical tapscript encoding.

type CheckpointPolicy

type CheckpointPolicy struct {
	// OperatorKey is the public key required by the operator-controlled
	// CSV unroll leaf.
	OperatorKey *btcec.PublicKey

	// CSVDelay is the relative timelock enforced by the
	// operator-controlled leaf.
	//
	// This is a raw BIP-68 sequence value interpreted by
	// OP_CHECKSEQUENCEVERIFY.
	CSVDelay uint32
}

CheckpointPolicy defines the parameters for constructing an OOR checkpoint taproot tree.

This is intentionally interface-first and minimal: it provides enough information to deterministically derive a checkpoint output pkScript, while allowing the underlying closure system to evolve later.

type CompiledPolicy

type CompiledPolicy struct {
	// InternalKey is the (unspendable) internal key for this policy.
	InternalKey *btcec.PublicKey

	// Leaves are the policy leaves in canonical order.
	Leaves []PolicyLeaf

	// RootHash is the merkle root of the canonical tap tree.
	RootHash []byte
	// contains filtered or unexported fields
}

CompiledPolicy represents a fully compiled Ark policy with canonical leaf ordering, merkle tree structure, and derived spend information.

func BuildTree

func BuildTree(leaves []PolicyLeaf,
	internalKey *btcec.PublicKey) (*CompiledPolicy, error)

BuildTree constructs a canonical balanced binary taproot tree from the ordered leaf list using the Ark NUMS internal key. The NUMS key ensures key-path spends are impossible — all spending goes through script leaves.

This implements the algorithm from the RFC:

  • If n == 1: the root is the single leaf hash.
  • If n > 1: split left = leaves[0:n/2], right = leaves[n/2:n], compute L = BuildTree(left), R = BuildTree(right), root = TapBranchHash(min(L,R), max(L,R)).

The function also computes merkle proofs for each leaf.

func (*CompiledPolicy) OutputKey

func (p *CompiledPolicy) OutputKey() *btcec.PublicKey

OutputKey computes the taproot output key for this policy.

func (*CompiledPolicy) ScriptIndex

func (p *CompiledPolicy) ScriptIndex(script []byte) int

ScriptIndex returns the canonical leaf index for the given script, or -1 if not found.

func (*CompiledPolicy) SpendInfo

func (p *CompiledPolicy) SpendInfo(leafIndex int) (*SpendInfo, error)

SpendInfo returns the spend information for the leaf at the given index.

func (*CompiledPolicy) SpendInfoForNode

func (p *CompiledPolicy) SpendInfoForNode(node Node) (*SpendInfo, error)

SpendInfoForNode returns the witness-level spend info (script + control block) for the leaf corresponding to the given AST node. The canonical leaf index is resolved via ScriptIndex.

func (*CompiledPolicy) SpendPathForNode

func (p *CompiledPolicy) SpendPathForNode(node Node, conditions [][]byte) (
	*SpendPath, error)

SpendPathForNode returns a full SpendPath for the leaf corresponding to the given AST node, including tx-context (sequence/locktime) derived from the AST and any provided condition witnesses.

type ComposedPolicy

type ComposedPolicy struct {
	// InternalKey is the (unspendable) internal key for this policy.
	InternalKey *btcec.PublicKey

	// PolicyRoot is the merkle root of the Ark policy subtree.
	PolicyRoot chainhash.Hash

	// ExternalRoot is the externally provided root to combine with.
	ExternalRoot chainhash.Hash

	// CombinedRoot is the final merkle root after composition.
	CombinedRoot chainhash.Hash

	// ArkPolicy is the underlying Ark policy for spend info retrieval.
	ArkPolicy *CompiledPolicy
}

ComposedPolicy represents a policy that combines an Ark policy root with an external root (e.g., from Taproot Assets).

func ComposeWithSiblingRoot

func ComposeWithSiblingRoot(policy *CompiledPolicy,
	externalRoot chainhash.Hash) (*ComposedPolicy, error)

ComposeWithSiblingRoot combines an Ark policy with an external root. The combined root is computed as:

TapBranchHash(min(policyRoot, extRoot), max(policyRoot, extRoot))

The internal key must be unspendable (no key-path spend).

func (*ComposedPolicy) OutputKey

func (c *ComposedPolicy) OutputKey() *btcec.PublicKey

OutputKey computes the taproot output key for this composed policy.

func (*ComposedPolicy) SpendInfo

func (c *ComposedPolicy) SpendInfo(leafIndex int) (*SpendInfo, error)

SpendInfo returns the spend information for a leaf in the Ark policy subtree. The control block's merkle proof will include the external root as an additional sibling at the root level.

type Condition

type Condition struct {
	// Predicate is a canonical script fragment prepended ahead of the
	// inner spending clause. The fragment is expected to enforce its
	// own VERIFY/DROP semantics as needed.
	Predicate []byte

	// Inner is the expression gated by the predicate.
	Inner Node
}

Condition represents a generic opaque script prefix that must execute before the inner spending clause is evaluated. Canonical encoding: <prefix> <inner>.

func (*Condition) Script

func (c *Condition) Script() ([]byte, error)

Script compiles the Condition node to its canonical tapscript encoding.

type EncodedLeaf

type EncodedLeaf struct {
	// Depth is the depth of this leaf in the tree (root is depth 0).
	Depth uint8

	// LeafVersion is the BIP-341 leaf version (typically 0xc0).
	LeafVersion uint8

	// Script is the tapscript leaf script bytes.
	Script []byte
}

EncodedLeaf represents a single leaf in the PSBT tap tree encoding.

func DecodeTapTree

func DecodeTapTree(data []byte) ([]EncodedLeaf, error)

DecodeTapTree deserializes a PSBT tap tree encoding back into leaf data. This can be used during PSBT finalization to reconstruct the tap tree.

type LeafTemplate

type LeafTemplate struct {
	// Node is the semantic AST that compiles into the tapscript leaf.
	Node Node
}

LeafTemplate is the semantic representation of one policy leaf. Currently this always compiles to a base-leaf-version (0xc0) tapscript. If leaf-versioned policies are needed in the future, this type would need a LeafVersion field.

func DecodeLeafTemplate

func DecodeLeafTemplate(raw []byte) (*LeafTemplate, error)

DecodeLeafTemplate deserializes a binary leaf template using the default decode budget. It rejects blobs larger than MaxLeafTemplateBytes and caps AST recursion via MaxPolicyDepth/MaxPolicyNodes.

func (LeafTemplate) Encode

func (l LeafTemplate) Encode() ([]byte, error)

Encode serializes the semantic leaf into a stable binary encoding.

func (LeafTemplate) ParticipantKeys

func (l LeafTemplate) ParticipantKeys() []*btcec.PublicKey

ParticipantKeys returns the unique public keys referenced by the leaf.

func (LeafTemplate) Script

func (l LeafTemplate) Script() ([]byte, error)

Script compiles the semantic leaf into canonical tapscript bytes.

type Multisig

type Multisig struct {
	// Keys are the public keys that must all sign. Order is significant.
	Keys []*btcec.PublicKey
}

Multisig represents an N-of-N multi-signature check. All keys must sign for the script to succeed.

func (*Multisig) Script

func (m *Multisig) Script() ([]byte, error)

Script compiles the Multisig node to its canonical tapscript encoding.

type Node

type Node interface {
	// Script compiles the node to its canonical tapscript encoding.
	Script() ([]byte, error)
	// contains filtered or unexported methods
}

Node is the interface that all AST nodes must implement. Each node represents a spending condition that can be compiled to a tapscript.

func DecodeNode

func DecodeNode(raw []byte) (Node, error)

DecodeNode deserializes one semantic AST node from binary encoding using the default decode budget. Each nested node charges against MaxPolicyDepth and MaxPolicyNodes.

type PolicyLeaf

type PolicyLeaf struct {
	// Leaf is the compiled tapscript leaf (script bytes + leaf version).
	Leaf txscript.TapLeaf
}

PolicyLeaf represents a compiled tapscript leaf in canonical ordering.

func (*PolicyLeaf) CompareTo

func (l *PolicyLeaf) CompareTo(other *PolicyLeaf) int

CompareTo returns -1 if this leaf sorts before other, 1 if after, and 0 if they are equal. Ordering is by leaf version first, then lexicographic by script bytes.

type PolicyTemplate

type PolicyTemplate struct {
	// Leaves are the semantic tapscript leaves in this policy.
	Leaves []LeafTemplate
}

PolicyTemplate is the semantic representation of an Ark tapscript policy.

func DecodePolicyTemplate

func DecodePolicyTemplate(raw []byte) (*PolicyTemplate, error)

DecodePolicyTemplate deserializes a binary policy template using the default decode budget. It rejects blobs larger than MaxPolicyTemplateBytes, policies with more than MaxPolicyLeaves leaves, and ASTs that exceed MaxPolicyDepth recursion or MaxPolicyNodes total nodes. The budget is shared across all leaves so a crafted blob cannot amplify across leaves.

func StandardVTXOTemplate

func StandardVTXOTemplate(ownerKey, operatorKey *btcec.PublicKey,
	exitDelay uint32) (*PolicyTemplate, error)

StandardVTXOTemplate builds the semantic policy template for the standard Ark VTXO/boarding output shape.

func (*PolicyTemplate) Compile

func (p *PolicyTemplate) Compile() (*CompiledPolicy, error)

Compile builds the canonical compiled policy from the semantic template.

func (*PolicyTemplate) Encode

func (p *PolicyTemplate) Encode() ([]byte, error)

Encode serializes the semantic policy template into a binary encoding. The encoding preserves the author's leaf order (not canonical order). Two templates with the same leaves in different order will produce different encoded bytes but identical compiled output keys.

func (*PolicyTemplate) MatchesPkScript

func (p *PolicyTemplate) MatchesPkScript(pkScript []byte) bool

MatchesPkScript compiles the policy and checks whether it matches the given output script.

func (*PolicyTemplate) ParticipantKeys

func (p *PolicyTemplate) ParticipantKeys() []*btcec.PublicKey

ParticipantKeys returns the unique public keys referenced by the policy. The returned slice is sorted by x-only key bytes for deterministic output.

func (*PolicyTemplate) PkScript

func (p *PolicyTemplate) PkScript() ([]byte, error)

PkScript compiles the semantic policy into its canonical P2TR output script.

func (*PolicyTemplate) SettlementPairsForParticipant

func (p *PolicyTemplate) SettlementPairsForParticipant(
	participant, operator *btcec.PublicKey) ([]SettlementPair, error)

SettlementPairsForParticipant derives the settlement pairs available to the given participant within the semantic policy.

func (*PolicyTemplate) ValidateArkPolicy

func (p *PolicyTemplate) ValidateArkPolicy(
	opts PolicyValidationOpts,
) error

ValidateArkPolicy enforces Ark policy invariants on the semantic template.

type PolicyValidationOpts

type PolicyValidationOpts struct {
	// OperatorKey is the operator's public key. Every collab leaf
	// must contain this key for the operator to safely co-sign.
	OperatorKey *btcec.PublicKey

	// MinExitDelay is the minimum CSV delay required on exit leaves
	// (in blocks). When zero, the CSV-minimum check is skipped —
	// callers that admit standard VTXO policies should use
	// ValidateStandardVTXOPolicy instead, which requires MinExitDelay
	// to be set and refuses zero fail-closed.
	MinExitDelay uint32
}

PolicyValidationOpts configures VTXO policy validation against Ark invariants.

type SettlementPair

type SettlementPair struct {
	// ParticipantKey is the participant key this settlement
	// pair belongs to.
	ParticipantKey *btcec.PublicKey

	// AuthLeafIndex is the canonical leaf index of the
	// unilateral auth path.
	AuthLeafIndex int

	// AuthPath is the unilateral proof/auth spend path for the participant.
	AuthPath *SpendPath

	// ForfeitLeafIndex is the canonical leaf index of the
	// operator-backed path.
	ForfeitLeafIndex int

	// ForfeitPath is the spend path used by the later round forfeit tx.
	ForfeitPath *SpendPath
}

SettlementPair captures the unilateral proof/auth path and the paired operator-backed forfeit path for one participant branch of a custom policy.

type SpendInfo

type SpendInfo struct {
	// WitnessScript is the tapscript leaf script bytes.
	WitnessScript []byte

	// ControlBlock is the BIP-341 control block for script-path spending.
	ControlBlock []byte
}

SpendInfo contains the witness-level data needed to spend a specific leaf in an Ark policy: the tapscript and its BIP-341 control block. Transaction-level context (nSequence, nLockTime) is NOT included here — that is derived from the AST node by the spending tx builder or carried on SpendPath for durable artifacts.

func NewVTXOSpendInfoFromPolicy

func NewVTXOSpendInfoFromPolicy(ownerKey, cosignerKey *btcec.PublicKey,
	exitDelay uint32, leafIndex int) (*SpendInfo, error)

NewVTXOSpendInfoFromPolicy derives the spend information for the specified leaf index from a VTXOPolicy. This is a convenience wrapper for callers migrating from the scripts.NewVTXOSpendInfo API.

IMPORTANT: The leafIndex here uses the legacy semantic ordering (0=collab, 1=exit), NOT the canonical VTXOPolicy.Leaves index. New callers should use VTXOPolicy.CollabSpendInfo/ExitSpendInfo directly instead.

func (*SpendInfo) BuildSignDescriptor

func (s *SpendInfo) BuildSignDescriptor(keyDesc keychain.KeyDescriptor,
	output *wire.TxOut, sigHashes *txscript.TxSigHashes,
	prevFetcher txscript.PrevOutputFetcher,
	inputIndex int) *input.SignDescriptor

BuildSignDescriptor returns the sign descriptor needed to sign for a leaf path using the provided spend info. The key descriptor should correspond to the key needed for the specific leaf being signed.

func (*SpendInfo) CollabWitness

func (s *SpendInfo) CollabWitness(ownerSig, cosignerSig input.Signature) (
	wire.TxWitness, error)

CollabWitness constructs the witness stack needed to spend the collaborative path of a VTXO output using the two signatures.

The witness stack ordering is:

<cosigner_sig> <owner_sig> <script> <control_block>

func (*SpendInfo) TimeoutWitness

func (s *SpendInfo) TimeoutWitness(sig input.Signature) (wire.TxWitness,
	error)

TimeoutWitness constructs the witness stack needed to spend the timeout path of a VTXO output.

The witness stack ordering is:

<owner_sig> <script> <control_block>

type SpendPath

type SpendPath struct {
	// SpendInfo is the compiled leaf script + control block.
	*SpendInfo

	// RequiredSequence is the BIP-68 sequence value required for
	// this leaf (e.g., CSV delay). 0xffffffff means no constraint.
	RequiredSequence uint32

	// RequiredLockTime is the nLockTime value required for this
	// leaf (e.g., CLTV locktime). 0 means no constraint.
	RequiredLockTime uint32

	// Conditions holds extra witness elements needed by the spend
	// script beyond signatures (e.g., preimage for hashlock).
	Conditions [][]byte
}

SpendPath bundles everything needed to spend a custom VTXO leaf through OOR. It includes witness data (script + control block), tx-context (sequence/locktime), and any condition witnesses.

func DecodeSpendPath

func DecodeSpendPath(raw []byte) (*SpendPath, error)

DecodeSpendPath deserializes a binary spend path encoding.

func (*SpendPath) AttachTapLeafScript

func (s *SpendPath) AttachTapLeafScript(in *psbt.PInput) error

AttachTapLeafScript adds the spend path's script and control block to a PSBT input so script-path signatures can be validated against the correct leaf.

func (*SpendPath) Encode

func (s *SpendPath) Encode() ([]byte, error)

Encode serializes the spend path into a stable binary encoding.

func (*SpendPath) SingleSigWitness

func (s *SpendPath) SingleSigWitness(sig input.Signature,
	sigHash txscript.SigHashType) (wire.TxWitness, error)

SingleSigWitness assembles a script-path witness for a single-signature leaf.

func (*SpendPath) Validate

func (s *SpendPath) Validate() error

Validate checks that the spend path contains the required script-path data.

func (*SpendPath) VerifyBindsToPkScript

func (s *SpendPath) VerifyBindsToPkScript(pkScript []byte) error

VerifyBindsToPkScript checks that the spend path's witness script and control block commit to a taproot output whose script is exactly the supplied pkScript. This is the binding check that prevents a caller from supplying a malformed control block for an unrelated taproot output and obtaining signatures against a script they did not prove ownership of.

The check runs:

  1. Parse the control block.
  2. Verify the internal key is the Ark NUMS point (no key-path spend).
  3. Compute the tap root hash from the witness script using the control block's inclusion proof.
  4. Tweak the NUMS internal key by the root hash to get the taproot output key.
  5. Build a P2TR script from the output key and compare to pkScript.

func (*SpendPath) Witness

func (s *SpendPath) Witness(sigItems ...[]byte) (wire.TxWitness, error)

Witness assembles a full script-path witness using the provided signatures, then appends any condition items, the witness script, and the control block.

type StandardVTXOParams

type StandardVTXOParams struct {
	// OwnerKey is the participant key on the collab and exit paths.
	OwnerKey *btcec.PublicKey

	// OperatorKey is the operator key on the collab path.
	OperatorKey *btcec.PublicKey

	// ExitDelay is the CSV delay on the unilateral exit path.
	ExitDelay uint32
}

StandardVTXOParams captures the semantic parameters of the standard Ark VTXO/boarding policy shape.

func DecodeStandardVTXOParams

func DecodeStandardVTXOParams(template *PolicyTemplate) (*StandardVTXOParams,
	error)

DecodeStandardVTXOParams validates that the semantic policy is a standard Ark VTXO policy and extracts its owner/operator/exit-delay tuple.

type VHTLCOpts

type VHTLCOpts struct {
	// Sender is the party initiating the HTLC (payer).
	Sender *btcec.PublicKey

	// Receiver is the party receiving the HTLC payment (payee).
	Receiver *btcec.PublicKey

	// Server is the Ark operator key.
	Server *btcec.PublicKey

	// PreimageHash is the SHA256 of the preimage.
	// This matches the Lightning Network payment hash format:
	// paymentHash = SHA256(preimage).
	PreimageHash lntypes.Hash

	// RefundLocktime is the absolute locktime for refund without
	// receiver (CLTV).
	RefundLocktime uint32

	// UnilateralClaimDelay is the CSV delay for unilateral claim path.
	UnilateralClaimDelay uint32

	// UnilateralRefundDelay is the CSV delay for unilateral refund
	// path.
	UnilateralRefundDelay uint32

	// UnilateralRefundWithoutReceiverDelay is the CSV delay for
	// unilateral refund without receiver.
	UnilateralRefundWithoutReceiverDelay uint32
}

VHTLCOpts contains the parameters for constructing a vHTLC policy.

type VHTLCPolicy

type VHTLCPolicy struct {
	// Template is the semantic policy template for this vHTLC.
	Template *PolicyTemplate

	// CompiledPolicy is the underlying compiled taproot tree.
	*CompiledPolicy

	// PreimageHash is the SHA256 hash the claim paths are locked to.
	PreimageHash lntypes.Hash

	// ClaimClosure through UnilateralRefundWithoutReceiverClosure
	// are the semantic AST nodes for each of the 6 leaves, used for
	// programmatic access and tx-context derivation. Canonical leaf
	// indices are derived on the fly via ScriptIndex.
	ClaimClosure                           Node
	RefundClosure                          Node
	RefundWithoutReceiverClosure           Node
	UnilateralClaimClosure                 Node
	UnilateralRefundClosure                Node
	UnilateralRefundWithoutReceiverClosure Node
}

VHTLCPolicy represents a compiled vHTLC taproot policy with 6 leaves: 3 collaborative (operator-cosigned) and 3 unilateral exit paths. Named accessors provide typed spend info with correct tx-context requirements derived from each closure's AST.

func NewVHTLCPolicy

func NewVHTLCPolicy(opts VHTLCOpts) (*VHTLCPolicy, error)

NewVHTLCPolicy constructs a vHTLC policy using the AST closure system.

The vHTLC has 6 leaves:

  1. Claim (collab): HashLock(preimage) + Multisig([receiver, server])
  2. Refund (collab): Multisig([sender, receiver, server])
  3. RefundWithoutReceiver (collab): CLTV(locktime) + Multisig([sender, server])
  4. UnilateralClaim (exit): CSV(delay) + HashLock(preimage) + Checksig(receiver)
  5. UnilateralRefund (exit): CSV(delay) + Multisig([sender, receiver])
  6. UnilateralRefundWithoutReceiver (exit): CSV(delay) + CLTV(locktime) + Checksig(sender)

func (*VHTLCPolicy) ClaimPath

func (p *VHTLCPolicy) ClaimPath(preimage lntypes.Preimage) (*SpendPath, error)

ClaimPath returns a SpendPath for claiming via the hashlock leaf. The preimage's SHA256 must match the policy's PreimageHash.

func (*VHTLCPolicy) ClaimSpendInfo

func (p *VHTLCPolicy) ClaimSpendInfo() (*SpendInfo, error)

ClaimSpendInfo returns the spend information for the Claim path.

func (*VHTLCPolicy) PkScript

func (p *VHTLCPolicy) PkScript() ([]byte, error)

PkScript returns the P2TR pkScript for the vHTLC output.

func (*VHTLCPolicy) RefundPath

func (p *VHTLCPolicy) RefundPath() (*SpendPath, error)

RefundPath returns a SpendPath for the cooperative refund.

func (*VHTLCPolicy) RefundSpendInfo

func (p *VHTLCPolicy) RefundSpendInfo() (*SpendInfo, error)

RefundSpendInfo returns the spend information for the Refund path.

func (*VHTLCPolicy) RefundWithoutReceiverPath

func (p *VHTLCPolicy) RefundWithoutReceiverPath() (*SpendPath, error)

RefundWithoutReceiverPath returns a SpendPath for the CLTV-gated refund without receiver.

func (*VHTLCPolicy) RefundWithoutReceiverSpendInfo

func (p *VHTLCPolicy) RefundWithoutReceiverSpendInfo() (*SpendInfo, error)

RefundWithoutReceiverSpendInfo returns the spend information for the RefundWithoutReceiver path.

func (*VHTLCPolicy) UnilateralClaimPath

func (p *VHTLCPolicy) UnilateralClaimPath(preimage lntypes.Preimage) (
	*SpendPath, error)

UnilateralClaimPath returns a SpendPath for claiming via the receiver-only CSV hashlock leaf. The preimage's SHA256 must match the policy's PreimageHash.

func (*VHTLCPolicy) UnilateralClaimSpendInfo

func (p *VHTLCPolicy) UnilateralClaimSpendInfo() (*SpendInfo, error)

UnilateralClaimSpendInfo returns the spend information for the UnilateralClaim path.

func (*VHTLCPolicy) UnilateralRefundSpendInfo

func (p *VHTLCPolicy) UnilateralRefundSpendInfo() (*SpendInfo, error)

UnilateralRefundSpendInfo returns the spend information for the UnilateralRefund path.

func (*VHTLCPolicy) UnilateralRefundWithoutReceiverPath

func (p *VHTLCPolicy) UnilateralRefundWithoutReceiverPath() (*SpendPath,
	error)

UnilateralRefundWithoutReceiverPath returns a SpendPath for the sender-only CSV+CLTV refund leaf.

func (*VHTLCPolicy) UnilateralRefundWithoutReceiverSpendInfo

func (p *VHTLCPolicy) UnilateralRefundWithoutReceiverSpendInfo() (*SpendInfo,
	error)

UnilateralRefundWithoutReceiverSpendInfo returns the spend information for the UnilateralRefundWithoutReceiver path.

type VTXOPolicy

type VTXOPolicy struct {
	// Template is the semantic policy template for this VTXO.
	Template *PolicyTemplate

	// CompiledPolicy is the underlying compiled taproot tree.
	*CompiledPolicy

	// OwnerKey is the key that owns this VTXO.
	OwnerKey *btcec.PublicKey

	// OperatorKey is the operator/cosigner key.
	OperatorKey *btcec.PublicKey

	// ExitDelay is the CSV delay for the exit path (in blocks).
	ExitDelay uint32
	// contains filtered or unexported fields
}

VTXOPolicy represents a compiled VTXO taproot policy with canonical structure and validated invariants.

func NewVTXOPolicy

func NewVTXOPolicy(ownerKey, operatorKey *btcec.PublicKey, exitDelay uint32) (
	*VTXOPolicy, error)

NewVTXOPolicy creates and validates a standard VTXO policy from the given parameters. The semantic template consists of: - Collab: 2-of-2 multisig requiring both owner and operator - Exit: CSV-gated 1-of-1 multisig for owner only

After canonical sorting, the actual leaf indices may differ from the template order. Use CollabSpendInfo/ExitSpendInfo to access the correct paths regardless of canonical position.

This produces byte-identical output to lib/scripts.VTXOTapScript().

func (*VTXOPolicy) CollabSpendInfo

func (v *VTXOPolicy) CollabSpendInfo() (*SpendInfo, error)

CollabSpendInfo returns the spend information for the collaborative path. Tx-context is derived from the collab AST node.

func (*VTXOPolicy) ExitSpendInfo

func (v *VTXOPolicy) ExitSpendInfo() (*SpendInfo, error)

ExitSpendInfo returns the spend information for the exit/timeout path. Tx-context is derived from the exit AST node.

type VTXOValidationError

type VTXOValidationError struct {
	Code    string
	Message string
}

VTXOValidationError represents a VTXO policy validation failure.

func (*VTXOValidationError) Error

func (e *VTXOValidationError) Error() string

Error implements the error interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL