Documentation
¶
Overview ¶
Package xchain implements the SeqDEX cross-chain atomic-swap mechanism (Phase 5, milestone 1): a "Design A" single-secret HTLC swap between a Bitcoin-script leg (a Bitcoin/Elements parent chain) and a Sequentia-asset leg (the anchored Sequentia chain).
Design A in one line: both legs are locked to the SAME hashlock H = sha256(secret) and the SAME secret. Redeeming either leg reveals the preimage on-chain, which the counterparty then uses to redeem the other — that single shared secret is what makes the swap atomic.
The locking script is identical on both chains (it is plain Bitcoin Script, which elementsd evaluates unchanged in Elements mode):
OP_IF
OP_SHA256 <H> OP_EQUALVERIFY <claim_pub> OP_CHECKSIG # redeem branch
OP_ELSE
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <refund_pub> OP_CHECKSIG # refund
OP_ENDIF
paid to P2SH. The redeem (IF) branch reveals the preimage; the refund (ELSE) branch spends back to the locker after nLockTime reaches <locktime> (CLTV).
The Sequentia value-add — the whole point of this milestone — is the "anchor-shortened ordering" enforced by the Swap orchestrator (see orchestrator.go): lock the BTC leg first, then require the Sequentia leg to land in a Sequentia block whose anchorheight >= the BTC-leg's block height (paper Principle 7). Because of Bitcoin anchoring, if the BTC leg is later reorged the SEQ leg reorgs with it, so the SEQ leg needs only ~1 confirmation with NO extra reorg-protection buffer.
Index ¶
- Variables
- func BitcoinChainParams(name string) (*chaincfg.Params, error)
- func NewCLNLNLeg(socketPath string) *clnLNLeg
- func P2SHHash160(redeemScript []byte) string
- type AnchorEvidence
- type AnchorStatus
- type BitcoinChain
- func (c *BitcoinChain) AddressScript(addr string) ([]byte, error)
- func (c *BitcoinChain) BlockCount() (int64, error)
- func (c *BitcoinChain) Broadcast(rawHex string) (string, error)
- func (c *BitcoinChain) Confirmations(txid string) (int, error)
- func (c *BitcoinChain) FeeRate() float64
- func (c *BitcoinChain) NewDestScript() ([]byte, error)
- func (c *BitcoinChain) Params() *chaincfg.Params
- func (c *BitcoinChain) RPC() *RPC
- func (c *BitcoinChain) RawTx(txid string) (string, error)
- func (c *BitcoinChain) RawTxAndConfirmations(txid string) (string, int, error)
- func (c *BitcoinChain) SetFeeRate(satVb float64)
- func (c *BitcoinChain) TxConfirmations(txid string) (int, error)
- type BitcoinLeg
- func (l *BitcoinLeg) BuildClaimTx(redeemScript []byte, in BitcoinSpendInput, key *Key) (string, error)
- func (l *BitcoinLeg) BuildRefundTx(redeemScript []byte, in BitcoinSpendInput, locktime uint32, key *Key) (string, error)
- func (l *BitcoinLeg) HTLCScript(claimPub, refundPub []byte, locktime uint32) ([]byte, error)
- func (l *BitcoinLeg) P2SHAddress(redeemScript []byte) (string, error)
- func (l *BitcoinLeg) P2SHScriptPubKey(redeemScript []byte) ([]byte, error)
- func (l *BitcoinLeg) Side() Leg
- func (l *BitcoinLeg) VerifyFundedHTLC(rawTxHex string, hashH, makerClaimPub, takerRefundPub []byte, ...) (*FundedBTCHTLC, error)
- type BitcoinSpendInput
- type Chain
- func (c *Chain) AddressScriptPubKey(addr string) (string, error)
- func (c *Chain) AssetBalance(assetID string) (uint64, error)
- func (c *Chain) BestBlockHash() (string, error)
- func (c *Chain) BlockAnchorHeight(blockHash string) (int64, error)
- func (c *Chain) BlockCertification(blockHash string) (certified, present bool, err error)
- func (c *Chain) BlockCount() (int64, error)
- func (c *Chain) BlockHashOfTx(txid string) (string, error)
- func (c *Chain) Broadcast(rawHex string) (string, error)
- func (c *Chain) Confirmations(txid string) (int, error)
- func (c *Chain) ExtractPreimage(txid string, wantHash []byte) ([]byte, error)
- func (c *Chain) FeeExchangeRate(assetHex string) (uint64, bool)
- func (c *Chain) GetAnchorStatus() (*AnchorStatus, error)
- func (c *Chain) LockHTLC(redeemScript []byte, amountCoins, assetLabel string) (*FundedHTLC, error)
- func (c *Chain) Mine(n int) error
- func (c *Chain) NewDestScript() ([]byte, error)
- func (c *Chain) OutputAt(txid string, vout uint32) (*ChainOutput, error)
- func (c *Chain) P2SHAddress(redeemScript []byte) (string, error)
- func (c *Chain) PeggedAsset() (string, error)
- func (c *Chain) RPC() *RPC
- func (c *Chain) RedeemScriptSigContains(txid, needleHex string) (bool, string, error)
- func (c *Chain) SpenderOf(txid string, vout uint32) (string, error)
- func (c *Chain) TryBroadcast(rawHex string) (string, error)
- func (c *Chain) TxConfirmations(txid string) (int, error)
- type ChainOutput
- type Direction
- type ElementsLeg
- func (l *ElementsLeg) HTLCScript(claimPub, refundPub []byte, locktime uint32) ([]byte, error)
- func (l *ElementsLeg) Redeem(redeemScript []byte, in ElementsSpendInput, key *Key) (string, error)
- func (l *ElementsLeg) Refund(redeemScript []byte, in ElementsSpendInput, locktime uint32, key *Key) (string, error)
- func (l *ElementsLeg) Side() Leg
- type ElementsSpendInput
- type FundedBTCHTLC
- type FundedHTLC
- type HashLock
- type Key
- type LNLeg
- type Leg
- type LegLock
- type LockPrimitive
- type NormalParams
- type NormalResult
- type Party
- type RPC
- type ReverseMakerSecretOffer
- type ReverseMakerSecretParams
- type ReverseParams
- type ReverseResult
- type SubAnchorEvidence
- type SubmarineSwap
- func (m *SubmarineSwap) AwaitInvoicePaid(label string, timeout time.Duration) (uint64, error)
- func (m *SubmarineSwap) AwaitReversePayment(label string, timeout time.Duration) (uint64, error)
- func (m *SubmarineSwap) MintInvoice(preimage []byte, amountMsat uint64, cltvExpiry uint32, ...) (string, error)
- func (m *SubmarineSwap) OfferReverseMakerSecret(p ReverseMakerSecretParams) (*ReverseMakerSecretOffer, error)
- func (m *SubmarineSwap) PayInvoice(bolt11 string, wantHash []byte, amountMsat uint64) ([]byte, error)
- func (m *SubmarineSwap) RefundReverseSEQ(seqLeg *LegLock, makerRefundKey *Key, nLockTime uint32, fee uint64) (string, error)
- func (m *SubmarineSwap) RunNormal(p NormalParams, makerClaimKey *Key, seqClaimFee uint64) (*NormalResult, error)
- func (m *SubmarineSwap) RunReverse(p ReverseParams) (*ReverseResult, error)
- func (m *SubmarineSwap) VerifySeqAnchorBuried(seqBlockHash string, minAnchorDepth int64) (*SubAnchorEvidence, error)
- type Swap
- func (s *Swap) ClaimBTCLeg(leg *LegLock, bobClaim *Key, fee uint64) (string, error)
- func (s *Swap) ClaimSEQLeg(leg *LegLock, aliceClaim *Key, fee uint64) (string, error)
- func (s *Swap) HashHex() string
- func (s *Swap) InjectSecret(secret []byte) error
- func (s *Swap) LockBTCLeg(claimPub, refundPub []byte, amountCoins string, locktime uint32) (*LegLock, int64, error)
- func (s *Swap) LockSEQLeg(claimPub, refundPub []byte, amountCoins, assetLabel string, locktime uint32) (*LegLock, string, error)
- func (s *Swap) RefundBTCLeg(leg *LegLock, makerRefund *Key, nLockTime uint32, fee uint64) (string, error)
- func (s *Swap) RefundSEQLeg(leg *LegLock, bobRefund *Key, nLockTime uint32, fee uint64) (string, error)
- func (s *Swap) SecretHex() string
- func (s *Swap) VerifyBTCLeg(hashH, makerClaimPub, takerRefundPub, providedScript []byte, ...) (*VerifiedBTCLeg, error)
- func (s *Swap) VerifySEQLeg(hashH, makerSeqClaimPub, takerSeqRefundPub, providedScript []byte, ...) (*VerifiedSEQLeg, error)
- func (s *Swap) VerifySeqLegSafe(seqBlockHash string, btcLegHeight int64) (*AnchorEvidence, error)
- func (s *Swap) WatchSEQClaim(seqLeg *LegLock) (claimTxid string, secret []byte, err error)
- type VerifiedBTCLeg
- type VerifiedSEQLeg
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAnchorOrdering is returned by the orchestrator when the Sequentia leg // does NOT satisfy the anchor-shortened ordering rule (its block's // anchorheight is below the BTC-leg's confirmation height, or the anchor // status is not "ok"). Proceeding would forfeit the reorg-safety that // anchoring provides, so the orchestrator refuses. ErrAnchorOrdering = errors.New("xchain: SEQ leg violates anchor-shortened ordering (anchorheight < BTC-leg height, or anchor not ok)") // ErrBTCLegInvalid is returned by the maker when the taker's claimed BTC leg // does not match the quote (wrong H, wrong redeemScript, wrong amount/asset, // or it does not pay the expected HTLC P2SH). The maker MUST refuse to lock // its SEQ leg in this case. ErrBTCLegInvalid = errors.New("xchain: taker BTC leg invalid (does not match quote)") // ErrBTCLegUnconfirmed is returned when the taker's BTC leg is not yet // confirmed to the required depth. The taker must confirm it before the // maker will lock the SEQ leg (the BTC-leg-first ordering rule). ErrBTCLegUnconfirmed = errors.New("xchain: taker BTC leg not confirmed to required depth") // ErrSEQLegInvalid is the reverse (asset->BTC) mirror of ErrBTCLegInvalid: // returned when the taker's funded SEQ asset leg does not match the quote // (wrong H, wrong redeemScript, wrong amount/asset, or it does not pay the // expected HTLC P2SH). The maker MUST refuse to reveal the secret (claim the // SEQ leg) in this case. ErrSEQLegInvalid = errors.New("xchain: taker SEQ leg invalid (does not match quote)") // ErrSEQLegUnconfirmed is the reverse mirror of ErrBTCLegUnconfirmed: the // taker's SEQ asset leg is not yet confirmed to the required depth. ErrSEQLegUnconfirmed = errors.New("xchain: taker SEQ leg not confirmed to required depth") // ErrLNLegInvalid means the Lightning leg of a submarine swap does not match // the swap (invoice hash != H, wrong amount, or a revealed preimage that does // not hash to H). The maker MUST NOT proceed. ErrLNLegInvalid = errors.New("xchain: submarine LN leg invalid (does not match swap)") // ErrLNLegTimeout means a Lightning-leg wait (e.g. a hold invoice never // reaching "accepted") exceeded its deadline; fall back to the refund path. ErrLNLegTimeout = errors.New("xchain: submarine LN leg timed out") )
Functions ¶
func BitcoinChainParams ¶
BitcoinChainParams selects btcd chain params for the BTC parent leg.
"regtest" -> RegressionNetParams (bcrt HRP, 0x6f/0xc4 prefixes) "testnet4" -> testNet4Params (tb HRP, testnet3 prefixes) "testnet3"/"testnet" -> TestNet3Params "mainnet" -> MainNetParams
regtest and testnet4 produce IDENTICAL transaction serialization (the task's "regtest == testnet4 format"); only address prefixes differ, and the maker's claim never needs to round-trip an address (it spends by outpoint).
func NewCLNLNLeg ¶
func NewCLNLNLeg(socketPath string) *clnLNLeg
NewCLNLNLeg builds a Lightning leg backed by the CLN node whose lightning-rpc socket is at socketPath (e.g. <lightning-dir>/<network>/lightning-rpc).
func P2SHHash160 ¶
P2SHHash160 returns hex(hash160(redeemScript)); handy for cross-checking the node's decodescript output. The funding P2SH address is obtained from the node via decodescript (see client.DecodeScriptP2SH) to avoid any address prefix mismatch.
Types ¶
type AnchorEvidence ¶
type AnchorEvidence struct {
BTCLegHeight int64
SeqBlockHash string
SeqBlockAnchor int64
AnchorStatus string
NodeAnchorHeight int64
Certified bool // the SEQ block is quorum-certified (immediately final)
CertPresent bool // the node reported certification (false = pre-upgrade node)
OK bool
}
AnchorEvidence captures what VerifySeqLegSafe checked, for proof/printing.
type AnchorStatus ¶
type AnchorStatus struct {
ValidateAnchor bool `json:"validateanchor"`
TipHeight int64 `json:"tipheight"`
AnchorHeight int64 `json:"anchorheight"`
AnchorHash string `json:"anchorhash"`
AnchorStatus string `json:"anchorstatus"`
}
AnchorStatus is the subset of getanchorstatus we care about.
type BitcoinChain ¶
type BitcoinChain struct {
// contains filtered or unexported fields
}
BitcoinChain is the maker's view of the REAL Bitcoin parent (bitcoind regtest or testnet4). It is the Bitcoin-format analog of Chain (chain.go, which is Elements-format): it talks to a genuine bitcoind over the same JSON-RPC plumbing (RPC) but parses Bitcoin transactions — 8-byte satoshi values, no asset commitments — and derives the HTLC P2SH address itself via btcd rather than via Elements decodescript.
Only the calls the maker's BTC leg needs are implemented: read the raw funding tx + its confirmations (to VERIFY the taker's HTLC), broadcast the claim, learn the destination scriptPubKey, the height, and detect the taker's SEQ-side claim is NOT here — that stays on the SEQ (Elements) Chain.
func NewBitcoinChain ¶
func NewBitcoinChain(rpc *RPC, wallet string, params *chaincfg.Params) *BitcoinChain
NewBitcoinChain binds a BitcoinChain to a bitcoind RPC + wallet and chain params (regtest/testnet4). The wallet is used for getnewaddress (claim destination) and, on regtest, generatetoaddress/sendtoaddress in the verification harness; HTLC verification/claim themselves are wallet-agnostic.
func (*BitcoinChain) AddressScript ¶
func (c *BitcoinChain) AddressScript(addr string) ([]byte, error)
AddressScript decodes a Bitcoin address (under this chain's params) and returns its scriptPubKey.
func (*BitcoinChain) BlockCount ¶
func (c *BitcoinChain) BlockCount() (int64, error)
BlockCount returns the current Bitcoin chain height.
func (*BitcoinChain) Broadcast ¶
func (c *BitcoinChain) Broadcast(rawHex string) (string, error)
Broadcast submits a raw Bitcoin tx hex and returns its txid.
func (*BitcoinChain) Confirmations ¶
func (c *BitcoinChain) Confirmations(txid string) (int, error)
Confirmations returns the number of confirmations of a Bitcoin tx via the wallet's gettransaction (works for wallet txs only); used by the harness.
func (*BitcoinChain) FeeRate ¶
func (c *BitcoinChain) FeeRate() float64
FeeRate returns the configured HTLC funding fee rate (sat/vB), 0 if unset.
func (*BitcoinChain) NewDestScript ¶
func (c *BitcoinChain) NewDestScript() ([]byte, error)
NewDestScript returns a fresh wallet address' scriptPubKey for the maker to receive its claimed BTC, derived locally from the address (no Elements validateaddress dependency). It accepts any standard Bitcoin address type the wallet hands out (P2PKH, P2SH, bech32 v0/v1).
func (*BitcoinChain) Params ¶
func (c *BitcoinChain) Params() *chaincfg.Params
Params returns the chain params this BTC leg uses.
func (*BitcoinChain) RPC ¶
func (c *BitcoinChain) RPC() *RPC
RPC exposes the underlying wallet-scoped client (for the harness / ad-hoc).
func (*BitcoinChain) RawTx ¶
func (c *BitcoinChain) RawTx(txid string) (string, error)
RawTx fetches a transaction's raw (Bitcoin-serialized) hex via getrawtransaction <txid> false. Works for any tx the node has indexed (txindex) or that is in a wallet/mempool — including the taker-funded HTLC the maker did not create.
func (*BitcoinChain) RawTxAndConfirmations ¶
func (c *BitcoinChain) RawTxAndConfirmations(txid string) (string, int, error)
RawTxAndConfirmations returns both the raw hex and the confirmation count in one verbose getrawtransaction (the maker needs both to VerifyFundedHTLC).
func (*BitcoinChain) SetFeeRate ¶
func (c *BitcoinChain) SetFeeRate(satVb float64)
SetFeeRate sets the sat/vB fee rate used when funding HTLC legs.
func (*BitcoinChain) TxConfirmations ¶
func (c *BitcoinChain) TxConfirmations(txid string) (int, error)
TxConfirmations returns a tx's confirmation count via the verbose getrawtransaction (Bitcoin returns "confirmations" only when the tx is in a block or watched; 0/absent => unconfirmed).
type BitcoinLeg ¶
type BitcoinLeg struct {
// contains filtered or unexported fields
}
BitcoinLeg builds and spends a Design-A HTLC on a REAL Bitcoin chain (regtest or testnet4) — as opposed to ElementsLeg, which only works against an Elements-mode parent. It is the maker's "real-bitcoind leg": when the parent is a genuine bitcoind, the taker funds/refunds the BTC HTLC with a real Bitcoin signer (btc.js in the browser wallet) and the maker must VERIFY and CLAIM that HTLC in Bitcoin transaction format.
The HTLC redeemScript is byte-for-byte the SAME generic Bitcoin Script the ElementsLeg uses (it comes from primitive.go's HashLock.LockScript). Only the transaction envelope differs:
- Bitcoin values are 8-byte little-endian satoshis; there are NO asset commitments and NO explicit fee output (fee = sum(in) - sum(out)).
- Serialization is Bitcoin wire format (wire.MsgTx), parsed/built with btcd, NOT go-elements.
- The legacy P2SH sighash is txscript.CalcSignatureHash with SIGHASH_ALL, and the spend is a legacy (non-segwit) P2SH scriptSig: <sig> <preimage> OP_TRUE <redeemScript> (redeem / IF branch) <sig> OP_FALSE <redeemScript> (refund / ELSE branch) — the SAME unlock-item order ElementsLeg uses (the RedeemUnlockItems / RefundUnlockItems from the shared LockPrimitive), with the redeemScript pushed last.
Address/serialization parameters are selectable for regtest vs testnet4 via BitcoinChainParams (testnet4 reuses testnet3's address prefixes + "tb" HRP, which is all the maker needs — it never relies on the network magic or the genesis hash for HTLC work).
func NewBitcoinLeg ¶
func NewBitcoinLeg(prim LockPrimitive, params *chaincfg.Params) *BitcoinLeg
NewBitcoinLeg returns a BTC-leg builder for the given hashlock primitive and chain params (use BitcoinChainParams to pick regtest/testnet4).
func (*BitcoinLeg) BuildClaimTx ¶
func (l *BitcoinLeg) BuildClaimTx(redeemScript []byte, in BitcoinSpendInput, key *Key) (string, error)
BuildClaimTx builds a signed redeem (IF-branch) spend revealing the preimage, paying (Amount-Fee) to DestPK. This is the maker's BTC-leg claim. Returns the serialized Bitcoin tx hex for sendrawtransaction.
func (*BitcoinLeg) BuildRefundTx ¶
func (l *BitcoinLeg) BuildRefundTx(redeemScript []byte, in BitcoinSpendInput, locktime uint32, key *Key) (string, error)
BuildRefundTx builds a signed refund (ELSE-branch) spend, valid once nLockTime reaches the CLTV locktime. The maker does NOT refund BTC in the implemented direction (the taker refunds its own BTC leg), but this is provided for symmetry with ElementsLeg.Refund and for completeness.
func (*BitcoinLeg) HTLCScript ¶
func (l *BitcoinLeg) HTLCScript(claimPub, refundPub []byte, locktime uint32) ([]byte, error)
HTLCScript renders the redeemScript for the given pubkeys/CLTV locktime. It is identical to ElementsLeg.HTLCScript (same generic Bitcoin Script).
func (*BitcoinLeg) P2SHAddress ¶
func (l *BitcoinLeg) P2SHAddress(redeemScript []byte) (string, error)
P2SHAddress derives the base58 P2SH address for a redeemScript using this leg's chain params. Unlike the Elements leg (which asks the node via decodescript), the maker derives the Bitcoin HTLC address itself so it never depends on the bitcoind wallet importing the script.
func (*BitcoinLeg) P2SHScriptPubKey ¶
func (l *BitcoinLeg) P2SHScriptPubKey(redeemScript []byte) ([]byte, error)
P2SHScriptPubKey returns the scriptPubKey (OP_HASH160 <h160> OP_EQUAL) that an output funding this redeemScript's P2SH must carry. Used by VerifyFundedHTLC to locate the HTLC output by exact scriptPubKey match.
func (*BitcoinLeg) Side ¶
func (l *BitcoinLeg) Side() Leg
Side reports which leg this builder serves (always the BTC leg).
func (*BitcoinLeg) VerifyFundedHTLC ¶
func (l *BitcoinLeg) VerifyFundedHTLC( rawTxHex string, hashH, makerClaimPub, takerRefundPub []byte, btcLocktime uint32, wantAmount uint64, confirmations, minConf int, ) (*FundedBTCHTLC, error)
VerifyFundedHTLC validates the taker's funded BTC-leg HTLC against the agreed parameters, working entirely in Bitcoin transaction format.
Given the raw funding tx (Bitcoin-serialized hex, e.g. from getrawtransaction) and the agreed HTLC params (hashH, makerClaimPub, takerRefundPub, btcLocktime, expected amount in sats), it:
- recomputes the canonical Design-A redeemScript from (hashH, claim, refund, locktime) — identical to what btc.js builds;
- computes that script's P2SH scriptPubKey under this leg's chain params;
- parses the funding tx as a wire.MsgTx (Bitcoin format, NOT go-elements) and locates the output paying that exact scriptPubKey;
- checks the output value equals the agreed amount and that the funding tx has at least minConf confirmations.
On success it returns the funded outpoint + value so the maker can later BuildClaimTx against it.
type BitcoinSpendInput ¶
type BitcoinSpendInput struct {
TxID string // funding txid (big-endian display order)
Vout uint32
Amount uint64 // value of the HTLC output, in satoshis
DestPK []byte // scriptPubKey of the redeem/refund destination
Fee uint64 // fee in satoshis (subtracted from Amount; no explicit fee output)
}
BitcoinSpendInput identifies the HTLC output being spent on the BTC leg.
type Chain ¶
type Chain struct {
// contains filtered or unexported fields
}
Chain wraps an RPC client with the higher-level operations a swap leg needs on one Elements-mode node: funding an HTLC P2SH output, locating the funded vout, fetching destination scripts, mining, broadcasting, and (for the Sequentia leg) reading anchor metadata.
func (*Chain) AddressScriptPubKey ¶
AddressScriptPubKey returns the hex scriptPubKey the node derives for an address (used to match the HTLC P2SH against the funded output).
func (*Chain) AssetBalance ¶
AssetBalance returns the wallet's confirmed balance of an asset, in atoms. assetID may be "" to query the default pegged "bitcoin" asset (the BTC leg's reserve). For an issued SEQ asset, pass its hex id. Uses getbalance with an explicit asset to avoid pulling in confidential-balance machinery.
func (*Chain) BestBlockHash ¶
BestBlockHash returns the current tip hash.
func (*Chain) BlockAnchorHeight ¶
BlockAnchorHeight returns the anchorheight recorded in a Sequentia block header (getblockheader <hash> true). This is the per-block anchor commitment the SEQ-side claimant checks against the BTC-leg height.
func (*Chain) BlockCertification ¶
BlockCertification reports whether a Sequentia block is quorum-certified (immediately final): its committee countersignatures reached the quorum. This is stronger than anchoring — anchoring binds the leg to Bitcoin, quorum certification means the committee finalized it. `present` is false against a node predating the getblockheader poscertified field, so the caller can keep anchor-only behavior during a rolling upgrade rather than break.
func (*Chain) BlockCount ¶
BlockCount returns the current chain height.
func (*Chain) BlockHashOfTx ¶
BlockHashOfTx returns the block hash that confirmed a tx (the SEQ-leg block whose anchorheight the orchestrator verifies). Uses getrawtransaction so it works for txs the local wallet did NOT create — e.g. the taker-funded SEQ leg the reverse maker must verify; gettransaction errors -5 ("Invalid or non-wallet transaction id") on a non-wallet txid. Relies on the node's txindex (the DEX node has it).
func (*Chain) Confirmations ¶
Confirmations returns a wallet tx's confirmation count.
func (*Chain) ExtractPreimage ¶
ExtractPreimage reads the preimage off a redeem spend's input-0 scriptSig. This is the maker's secret-extraction step: the daemon watches the SEQ chain for the taker's claim, then recovers the preimage `s` (the data push whose sha256 equals the swap's hashlock H) so it can claim the BTC leg with it.
It tokenizes every input's scriptSig and returns the first pushed data item whose SHA256 equals wantHash. Returning the raw bytes (rather than a contains-check) is what lets the maker actually USE the secret.
func (*Chain) FeeExchangeRate ¶
FeeExchangeRate returns the node's any-asset fee exchange rate for assetHex (the integer getfeeexchangerates publishes: the asset's value in native-sats x 1e8), and whether a positive rate is published. The rate map may be keyed by human label (asset registry) rather than hex, so resolve labels via dumpassetlabels. Returns (0,false) on any RPC error / missing asset / non-positive rate so callers fall back to the native fee. Used to size an asset-denominated fee so its native-equivalent value stays within the node's relay fee bounds (maxfeerate).
func (*Chain) GetAnchorStatus ¶
func (c *Chain) GetAnchorStatus() (*AnchorStatus, error)
GetAnchorStatus reads the node's current anchor view.
func (*Chain) LockHTLC ¶
func (c *Chain) LockHTLC(redeemScript []byte, amountCoins, assetLabel string) (*FundedHTLC, error)
LockHTLC pays `amountCoins` (a decimal string, e.g. "10") of the given asset to the HTLC's P2SH address and returns the funded outpoint. assetLabel may be "" to pay the chain's default (pegged "bitcoin") asset — that is how the BTC leg is funded.
func (*Chain) NewDestScript ¶
NewDestScript returns a fresh address' scriptPubKey to receive a redeem/refund.
func (*Chain) OutputAt ¶
func (c *Chain) OutputAt(txid string, vout uint32) (*ChainOutput, error)
OutputAt returns the (txid, vout) output of any on-chain tx.
func (*Chain) P2SHAddress ¶
P2SHAddress asks the node to derive the P2SH address for a redeemScript. Using the node's own decodescript avoids any address-prefix mismatch between btcd/go-elements encodings and the node's network parameters.
func (*Chain) PeggedAsset ¶
PeggedAsset returns the chain's pegged "bitcoin" asset id (used as the BTC leg's asset when building spends).
func (*Chain) RedeemScriptSigContains ¶
RedeemScriptSigContains reports whether the given on-chain spend's input 0 scriptSig asm contains the hex needle (used to prove the preimage was revealed on-chain, so the counterparty can read it).
func (*Chain) SpenderOf ¶
SpenderOf reports the txid that spent the given outpoint, or "" if it is still unspent. The maker uses this to detect the taker's SEQ-leg claim: when the SEQ-leg output is spent, the spender's scriptSig carries the preimage.
It uses gettxout (which returns null once an outpoint is spent) to learn that a spend happened, then scans the mempool + recent blocks for the spender.
func (*Chain) TryBroadcast ¶
TryBroadcast is like Broadcast but returns the node's rejection error (used by the negative refund-before-timeout test).
func (*Chain) TxConfirmations ¶
TxConfirmations returns a tx's confirmation count via getrawtransaction, which (unlike gettransaction) works for txs the local wallet did not create — e.g. the taker-funded BTC leg the maker must verify. Returns 0 if unconfirmed.
type ChainOutput ¶
ChainOutput is a tx output's value/asset/scriptPubKey, as the maker needs it to verify the taker's BTC-leg funding.
type Direction ¶
type Direction struct{}
Direction documents which way value flows. We implement and document ONE direction: the secret-holder (Alice) holds BTC and wants the SEQ asset; the counterparty (Bob) holds the SEQ asset and wants BTC. A single preimage, chosen by Alice, locks both legs (Design A).
Flow (with the anchor-shortened ordering as the headline rule):
- Alice (secret holder) LOCKS the BTC leg first. claim=Bob, refund=Alice, CLTV = btcLocktime (the LONGER timeout — Alice refunds last).
- Once the BTC leg is confirmed at parent height Hp, Bob LOCKS the SEQ leg. claim=Alice, refund=Bob, CLTV = seqLocktime (the SHORTER timeout).
- ORDERING CHECK (the Sequentia value-add): the SEQ leg must land in a Sequentia block whose anchorheight >= Hp, and getanchorstatus must be "ok". VerifySeqLegSafe enforces this; if it fails the orchestrator refuses to treat the SEQ leg as safe (ErrAnchorOrdering). Because of anchoring, once this holds the SEQ leg needs only ~1 confirmation with NO extra reorg buffer: if the BTC leg is later reorged, the SEQ block (anchored to the now-orphaned parent height) reorgs with it, reverting BOTH legs together — proven in test/functional/feature_anchor_swap_consistency.py.
- Alice REDEEMS the SEQ leg with the preimage (revealing it on-chain).
- Bob reads the preimage off Alice's SEQ redeem and REDEEMS the BTC leg.
If a counterparty stalls, the locker REFUNDS via the CLTV branch after its timeout (RefundPath).
type ElementsLeg ¶
type ElementsLeg struct {
// contains filtered or unexported fields
}
ElementsLeg builds and spends a Design-A HTLC on an Elements-mode chain. Both swap legs use it:
The "BTC" leg runs on the parent (anchor-source) node, paid in that chain's pegged "bitcoin" asset. Although we call it the BTC leg, the parent is an Elements node, so its transactions are Elements-serialized; the HTLC is nonetheless an ordinary Bitcoin-Script P2SH HTLC (the same OP_IF/OP_SHA256/OP_CLTV script), which elementsd evaluates unchanged. The script + spend logic are byte-identical to a real Bitcoin HTLC; only the transaction envelope is Elements.
The "SEQ" leg runs on the anchored Sequentia node, paid in an issued asset.
We deliberately use explicit (UNCONFIDENTIAL) HTLC outputs — confidential outputs (CT/blinding) are unnecessary for this mechanism proof and would only complicate the sighash. The redeemScript is leg-agnostic; the only per-leg data is the asset id of the output.
Script primitives (the redeemScript itself and the scriptSig assembly) come from btcd's txscript; the transaction body, the legacy sighash and value/ asset serialization come from go-elements. This split mirrors the package brief: btcd for Bitcoin-Script, go-elements for the Sequentia/Elements tx.
func NewElementsLeg ¶
func NewElementsLeg(leg Leg, prim LockPrimitive) *ElementsLeg
NewElementsLeg returns a leg builder for the given chain side.
func (*ElementsLeg) HTLCScript ¶
func (l *ElementsLeg) HTLCScript(claimPub, refundPub []byte, locktime uint32) ([]byte, error)
HTLCScript renders the redeemScript for the given pubkeys/CLTV locktime.
func (*ElementsLeg) Redeem ¶
func (l *ElementsLeg) Redeem(redeemScript []byte, in ElementsSpendInput, key *Key) (string, error)
Redeem builds a signed redeem (IF-branch) spend revealing the preimage.
func (*ElementsLeg) Refund ¶
func (l *ElementsLeg) Refund(redeemScript []byte, in ElementsSpendInput, locktime uint32, key *Key) (string, error)
Refund builds a signed refund (ELSE-branch) spend, valid once nLockTime reaches the CLTV locktime.
func (*ElementsLeg) Side ¶
func (l *ElementsLeg) Side() Leg
Leg reports which side this builder serves.
type ElementsSpendInput ¶
type ElementsSpendInput struct {
TxID string // funding txid (big-endian display order)
Vout uint32
Amount uint64 // value of the HTLC output, in atoms
AssetID string // 32-byte asset id, hex (big-endian display order)
DestSPK []byte // scriptPubKey of the redeem/refund destination
Fee uint64 // fee in atoms; emitted as an explicit fee output (empty SPK)
}
ElementsSpendInput identifies the HTLC output being spent on an Elements leg.
type FundedBTCHTLC ¶
type FundedBTCHTLC struct {
TxID string
Vout uint32
Amount uint64 // satoshis
Confirmations int
ScriptPubKey []byte // the P2SH scriptPubKey the output paid
}
FundedBTCHTLC is the maker's verified view of a real-Bitcoin HTLC funding.
type FundedHTLC ¶
FundedHTLC is the outpoint + value + asset of a funded HTLC output.
type HashLock ¶
type HashLock struct {
Hash []byte // 32-byte sha256(secret); the public part of the lock.
Secret []byte // 32-byte preimage; required to build a redeem spend.
}
HashLock is the Design-A SHA256 hashlock primitive. It holds the hash H of the swap secret; only the redeeming party needs Secret set, and only when actually building a redeem spend (RedeemUnlockItems checks it).
func NewHashLock ¶
NewHashLock builds a HashLock from a known preimage (the secret-holder side).
func NewHashLockFromHash ¶
NewHashLockFromHash builds a HashLock from only the hash (the counterparty side, before the secret is revealed on-chain).
func (*HashLock) LockScript ¶
LockScript renders the Design-A HTLC redeemScript. It is identical to contrib/sequentia/swap-demo.py's htlc_script() and to a real BTC<->SEQ HTLC.
func (*HashLock) RedeemUnlockItems ¶
RedeemUnlockItems: <sig> <preimage> OP_TRUE — selects the IF branch and satisfies SHA256(<preimage>)==H plus the claim-key CHECKSIG. (The leg appends the redeemScript push afterwards.)
type Key ¶
type Key struct {
// contains filtered or unexported fields
}
Key is a regtest-only EC keypair used to sign HTLC spends on either leg. Both legs use ECDSA over secp256k1 with a DER-encoded signature plus a 1-byte SIGHASH_ALL flag, so a single Key type serves both btcd and go-elements signing.
func KeyFromBytes ¶
KeyFromBytes reconstructs a Key from 32 secret bytes (e.g. read from a regtest key file — never pass secrets on the command line).
func (*Key) PubKey ¶
PubKey returns the 33-byte compressed public key, as embedded in the HTLC redeemScript.
type LNLeg ¶
type LNLeg interface {
// NodeID returns the node's Lightning id (hex pubkey).
NodeID() (string, error)
// Pay pays a BOLT11 whose payment_hash MUST equal wantHash and returns the
// preimage the payee revealed. The caller uses that preimage to redeem the
// SEQ leg. It fails if the invoice's hash != wantHash (so the maker never
// pays an invoice not tied to the swap secret).
Pay(bolt11 string, wantHash []byte, amountMsat uint64) (preimage []byte, err error)
// CreateHoldInvoice registers a hold invoice on paymentHash H for amountMsat
// with the given cltv/label/description and returns the BOLT11 for the taker
// to pay. Requires a holdinvoice plugin on the node.
CreateHoldInvoice(paymentHash []byte, amountMsat uint64, cltvExpiry uint32, label, description string) (bolt11 string, err error)
// WaitHeld blocks until the taker's HTLC for paymentHash is accepted-and-held
// (state "accepted") or the deadline. Returns the held amount (msat).
WaitHeld(paymentHash []byte, timeout time.Duration) (heldMsat uint64, err error)
// SettleHold resolves the held invoice with the preimage: the maker takes the
// incoming BTC-LN. Only call once the SEQ-side reveal is anchor-deep.
SettleHold(paymentHash, preimage []byte) error
// CancelHold fails the held invoice back to the payer (timeout / refund path).
CancelHold(paymentHash []byte) error
// CreateInvoice creates a plain BOLT11 whose payment_hash = SHA256(preimage)
// for amountMsat. The node knows the preimage, so it settles normally on
// payment (revealing P to the payer).
CreateInvoice(preimage []byte, amountMsat uint64, cltvExpiry uint32, label, description string) (bolt11 string, err error)
// WaitInvoicePaid blocks until the invoice with the given label is paid (or
// the deadline / invoice expiry), returning the received amount (msat).
WaitInvoicePaid(label string, timeout time.Duration) (paidMsat uint64, err error)
}
LNLeg abstracts the maker's Bitcoin-Lightning leg of a submarine swap. It is the LN analogue of btcBackend; it deliberately does NOT reuse btcBackend because an LN leg has no funded P2SH, txid or block height.
type Leg ¶
type Leg int
Leg identifies which chain a primitive is operating on. The two legs differ only in transaction serialization / sighash (Bitcoin-script vs Elements); the lock script itself is byte-for-byte identical, so a LockPrimitive is leg-agnostic and the leg-specific work lives in the *Leg builders.
type LegLock ¶
type LegLock struct {
Script []byte
Funded *FundedHTLC
Locktime uint32
}
LegLock records a funded HTLC leg.
type LockPrimitive ¶
type LockPrimitive interface {
// Kind is a short human label ("hashlock", "ptlc", ...).
Kind() string
// LockScript builds the redeemScript for the given claim/refund pubkeys
// and CLTV refund locktime.
LockScript(claimPub, refundPub []byte, locktime uint32) ([]byte, error)
// RedeemUnlockItems returns the stack items (excluding the trailing
// redeemScript push, which the leg adds) for the claim/IF branch, given a
// signature already produced over the spend's sighash and any
// primitive-specific secret material.
RedeemUnlockItems(sig []byte) ([][]byte, error)
// RefundUnlockItems returns the stack items (excluding the trailing
// redeemScript push) for the refund/ELSE branch, given a signature over
// the spend's sighash.
RefundUnlockItems(sig []byte) ([][]byte, error)
}
LockPrimitive abstracts the cryptographic lock used by a swap leg. Today the only implementation is HashLock (a SHA256 hashlock HTLC), but the swap orchestration in orchestrator.go is written purely against this interface so a PTLC / adaptor-signature primitive can be slotted in later without touching the orchestration.
A primitive produces three artefacts, all leg-agnostic raw scripts:
- LockScript: the redeemScript funded by both parties (-> P2SH address).
- RedeemUnlockItems: the data items that satisfy the "claim/IF" branch given a signature over the spend (e.g. <sig> <preimage> 1 for a hashlock; just <sig> for a PTLC). The leg builder is responsible for wrapping these into a scriptSig/witness together with the redeemScript.
- RefundUnlockItems: the data items that satisfy the "refund/ELSE" branch (e.g. <sig> 0 for the CLTV refund).
Splitting "what unlocks the branch" (here) from "how it is serialized into a scriptSig vs a witness" (the leg) is what keeps the abstraction clean across btcd and go-elements.
type NormalParams ¶
type NormalParams struct {
HashH []byte // the shared hashlock (== the invoice payment_hash)
MakerSeqClaimPub []byte // maker's SEQ-leg claim pubkey (maker claims with P)
TakerSeqRefundPub []byte // taker's SEQ-leg refund pubkey
SeqRedeemScript []byte // the taker's SEQ HTLC redeemScript (verified byte-for-byte)
SeqLocktime uint32 // the SEQ HTLC CLTV height (taker's refund branch)
SeqTxID string // the taker's SEQ HTLC funding outpoint
SeqVout uint32
SeqAmountAtoms uint64 // expected SEQ-leg value
SeqAssetID string // expected SEQ-leg asset id (internal hex)
SeqMinConf int // min Sequentia confirmations before considering the leg
Bolt11 string // the invoice the maker pays (reveals P)
InvoiceMsat uint64 // expected invoice amount (0 = don't cross-check)
MinAnchorDepth int64 // Bitcoin-anchor depth required before paying (> 1)
AnchorTimeout time.Duration
}
NormalParams is the maker's input for a NORMAL submarine swap. The taker has already funded the SEQ asset HTLC (claim=maker, refund=taker) and given the maker the BOLT11 it wants paid (payment_hash = H).
type NormalResult ¶
type NormalResult struct {
Anchor *SubAnchorEvidence
Preimage []byte // P, learned from paying the BOLT11
SeqClaimTxID string // the maker's SEQ-asset claim (spent with P)
}
NormalResult reports the outcome of a completed NORMAL swap.
type Party ¶
type Party struct {
// Alice = secret holder, funds BTC, receives SEQ.
AliceClaimSEQ *Key // Alice claims the SEQ leg
AliceRefundBTC *Key // Alice refunds the BTC leg she funded
// Bob = counterparty, funds SEQ, receives BTC.
BobClaimBTC *Key // Bob claims the BTC leg
BobRefundSEQ *Key // Bob refunds the SEQ leg he funded
}
Party holds the per-party keys for a swap. claim keys sign the IF branch on the leg that party receives; refund keys sign the ELSE branch on the leg that party funded.
type RPC ¶
type RPC struct {
// contains filtered or unexported fields
}
RPC is a minimal Elements/Bitcoin JSON-RPC client over HTTP, scoped to the calls the swap orchestrator needs. It is intentionally self-contained (rather than reusing pkg/explorer/elements) so the xchain mechanism has no dependency on the daemon's explorer types.
func (*RPC) Call ¶
Call invokes method with positional params and unmarshals the result into out (if non-nil).
func (*RPC) CallNamed ¶
CallNamed invokes method with a named-parameter object (Bitcoin/Elements JSON-RPC supports {"params": {...}}); used where positional args are awkward, e.g. sendtoaddress with a non-default assetlabel.
func (*RPC) WithWallet ¶
WithWallet returns a shallow copy targeting the named wallet endpoint.
type ReverseMakerSecretOffer ¶
type ReverseMakerSecretOffer struct {
Bolt11 string
HashH []byte
SeqLeg *LegLock
SeqBlock string // the Sequentia block that confirmed the asset HTLC (for the taker's anchor gate)
Label string
AmountSat uint64
}
ReverseMakerSecretOffer is what the maker hands the taker: the invoice to pay and the funded asset HTLC to verify + anchor-gate before paying.
type ReverseMakerSecretParams ¶
type ReverseMakerSecretParams struct {
TakerSeqClaimPub []byte // taker's SEQ-leg claim pubkey (taker claims with P)
MakerSeqRefundPub []byte // maker's SEQ-leg refund pubkey (reclaim after CLTV)
SeqLocktime uint32 // the SEQ HTLC CLTV height (maker's refund branch)
SeqAmountCoins string // the SEQ asset amount to lock (decimal string)
SeqAssetLabel string // the SEQ asset to lock
InvoiceMsat uint64 // the BTC-LN amount to invoice
InvoiceCLTV uint32 // the invoice's min_final_cltv_expiry
InvoiceLabel string // a unique label for the invoice (also used to await payment)
InvoiceDesc string // the invoice description
}
ReverseMakerSecretParams is the maker's input for the plugin-free reverse mode. The SubmarineSwap MUST have been built with a full HashLock (NewHashLock(P)) so the maker knows P.
type ReverseParams ¶
type ReverseParams struct {
HashH []byte // the shared hashlock (taker generated P; maker knows only H until reveal)
TakerSeqClaimPub []byte // taker's SEQ-leg claim pubkey (taker claims with P)
MakerSeqRefundPub []byte // maker's SEQ-leg refund pubkey (maker reclaims after timeout)
SeqLocktime uint32 // the SEQ HTLC CLTV height (maker's refund branch)
SeqAmountCoins string // the SEQ asset amount to lock (decimal string)
SeqAssetLabel string // the SEQ asset to lock
InvoiceMsat uint64 // the BTC-LN amount to hold-invoice
InvoiceCLTV uint32 // the hold invoice's min_final_cltv_expiry
InvoiceLabel string // a unique label for the hold invoice
InvoiceDesc string // the hold invoice description
HeldTimeout time.Duration // how long to wait for the taker's LN HTLC to be held
MinAnchorDepth int64 // Bitcoin-anchor depth of the SEQ claim required before settling (> 1)
ClaimTimeout time.Duration // how long to wait for the taker's on-chain SEQ claim
AnchorTimeout time.Duration // how long to wait for that claim to be anchor-buried
}
ReverseParams is the maker's input for a REVERSE submarine swap (the DEX taker buys the asset with BTC-LN). The taker generated the secret and sent the maker H; the maker issues a hold invoice on H and locks the SEQ asset HTLC (claim=taker, refund=maker).
type ReverseResult ¶
type ReverseResult struct {
Bolt11 string // the hold invoice the taker paid
HeldMsat uint64 // the BTC-LN amount held
SeqLeg *LegLock
SeqClaimTxID string // the taker's on-chain SEQ claim (revealed P)
Anchor *SubAnchorEvidence
Preimage []byte // P, extracted from the taker's SEQ claim
Settled bool // the maker settled the held BTC-LN invoice
}
ReverseResult reports the outcome of a completed REVERSE swap.
type SubAnchorEvidence ¶
type SubAnchorEvidence struct {
SeqBlockHash string
SeqBlockAnchor int64 // the SEQ block's recorded Bitcoin-anchor height
NodeAnchorHeight int64 // the node's current Bitcoin-anchor tip
AnchorDepth int64 // NodeAnchorHeight - SeqBlockAnchor (Bitcoin blocks burying the anchor)
MinAnchorDepth int64
AnchorStatus string
Certified bool
CertPresent bool
OK bool
}
SubAnchorEvidence records what the submarine anchor-depth gate checked.
type SubmarineSwap ¶
type SubmarineSwap struct {
*Swap // SEQ-leg operations + shared hashlock (btcBackend nil, unused)
// contains filtered or unexported fields
}
SubmarineSwap orchestrates a SeqLN Phase 2 submarine swap: a Sequentia asset ON-CHAIN leg <-> BTC over VANILLA Lightning, bound by one shared SHA256 preimage (Case A; design in SequentiaByClaude/doc/sequentia/seqln-phase2-submarine-swaps.md).
It REUSES the on-chain SEQ leg unchanged: it embeds *Swap purely for its SEQ-leg operations (VerifySEQLeg / LockSEQLeg / ClaimSEQLeg / RefundSEQLeg / WatchSEQClaim / InjectSecret) and the shared hashlock. The BTC leg is NOT an on-chain HTLC here, so the embedded Swap's btcBackend is nil and never used; the BTC leg is the LNLeg instead.
The maker plays the Boltz role: non-custodial, runs a SeqLN/CLN node on Bitcoin, and is the Sequentia on-chain HTLC counterparty. Worst case is a timelock refund on each leg.
The one Sequentia-specific safety rule (design §3.2) is the anchor-depth secret-reveal gate: because a Sequentia tx is final only to its Bitcoin-anchor depth, the maker must not take the IRREVERSIBLE Lightning action until the relevant Sequentia tx is buried by min_anchor_depth Bitcoin blocks. Unlike the same-block DEX ordering (VerifySeqLegSafe, deliberately 0-conf on the anchor axis), a submarine cross-leg point needs a real depth > 1 (min_anchor_depth):
- NORMAL: gate the SEQ *funding* before the maker pays the BOLT11.
- REVERSE: gate the taker's SEQ *claim* (which revealed P) before the maker settles the held BTC-LN invoice.
func NewSubmarineSwap ¶
func NewSubmarineSwap(seq *Chain, ln LNLeg, prim *HashLock) *SubmarineSwap
NewSubmarineSwap wires a submarine orchestrator to the anchored Sequentia node (SEQ leg) and a Bitcoin-Lightning leg (a SeqLN/CLN node on Bitcoin). prim is the shared hashlock: for a NORMAL swap the taker holds the secret and the maker learns it by paying the invoice; for a REVERSE swap the taker holds the secret and reveals it by claiming the SEQ asset on-chain.
func (*SubmarineSwap) AwaitInvoicePaid ¶
AwaitInvoicePaid blocks until the invoice with the given label is paid on THIS node (or the deadline). Generic sibling of AwaitReversePayment.
func (*SubmarineSwap) AwaitReversePayment ¶
AwaitReversePayment blocks until the taker pays the invoice (label), returning the received amount (msat). After this the taker holds P (learned from paying) and can claim the asset; if it never returns before SeqLocktime the maker reclaims the asset via RefundReverseSEQ.
func (*SubmarineSwap) MintInvoice ¶
func (m *SubmarineSwap) MintInvoice(preimage []byte, amountMsat uint64, cltvExpiry uint32, label, description string) (string, error)
MintInvoice creates a plain BOLT11 on THIS node bound to preimage (payment_hash = SHA256(preimage)). Used by the NORMAL-direction TAKER, which chooses P and mints the invoice it wants the maker to pay (paying it delivers the taker's BTC-LN and reveals P to the maker so it can claim the asset). Delegates to the LN leg's CreateInvoice.
func (*SubmarineSwap) OfferReverseMakerSecret ¶
func (m *SubmarineSwap) OfferReverseMakerSecret(p ReverseMakerSecretParams) (*ReverseMakerSecretOffer, error)
OfferReverseMakerSecret performs the maker's side of the plugin-free reverse swap up to the point the taker must act: it locks the asset HTLC (claim=taker) and issues a plain invoice on H = SHA256(P). The maker must know P (the swap was built with NewHashLock(P)). The returned offer carries everything the taker needs to verify the HTLC and run its own anchor-depth gate before paying.
func (*SubmarineSwap) PayInvoice ¶
func (m *SubmarineSwap) PayInvoice(bolt11 string, wantHash []byte, amountMsat uint64) ([]byte, error)
PayInvoice pays a BOLT11 whose payment_hash MUST equal wantHash and returns the revealed preimage. Used by the REVERSE-direction TAKER, which pays the maker's invoice and thereby LEARNS P (then claims the asset with it). It refuses any invoice not bound to the swap H. Delegates to the LN leg's Pay.
func (*SubmarineSwap) RefundReverseSEQ ¶
func (m *SubmarineSwap) RefundReverseSEQ(seqLeg *LegLock, makerRefundKey *Key, nLockTime uint32, fee uint64) (string, error)
RefundReverseSEQ builds the maker's SEQ-leg CLTV refund for a REVERSE swap that stalled (taker paid neither leg through, or the claim never buried). It is a thin pass-through to RefundSEQLeg so callers drive the whole refund path (CancelHold on the LN side + this on the SEQ side) from the SubmarineSwap.
func (*SubmarineSwap) RunNormal ¶
func (m *SubmarineSwap) RunNormal(p NormalParams, makerClaimKey *Key, seqClaimFee uint64) (*NormalResult, error)
RunNormal executes the maker side of a NORMAL submarine swap:
- VERIFY the taker's funded SEQ HTLC matches the quote (claimable by maker).
- WAIT for the SEQ *funding* to be anchor-buried to MinAnchorDepth. Only then is it safe for the maker to take the irreversible Lightning action, because once the funding is that deep it will not reorg out from under the claim.
- PAY the BOLT11 -> learn P (the maker is now committed on the LN side).
- CLAIM the SEQ asset with P (the maker holds the claim key).
The maker never pays the invoice until step 2 passes, so a Bitcoin reorg cannot leave it out-of-pocket on LN with an un-fundable SEQ claim.
func (*SubmarineSwap) RunReverse ¶
func (m *SubmarineSwap) RunReverse(p ReverseParams) (*ReverseResult, error)
RunReverse executes the maker side of a REVERSE submarine swap:
- CREATE a hold invoice on H (the maker will settle it only with P).
- WAIT for the taker's LN HTLC to be HELD (accepted, pending). Only after the BTC-LN is inbound-and-held does the maker put its own asset at risk.
- LOCK the SEQ asset HTLC (claim=taker with P, refund=maker after timeout).
- WATCH for the taker's on-chain SEQ claim and EXTRACT P from it.
- WAIT for that SEQ claim to be anchor-buried to MinAnchorDepth. THE gate: the maker must not settle the BTC-LN leg until the SEQ claim that revealed P is anchor-deep, or a Bitcoin reorg could revert the SEQ claim after the maker has already taken the BTC-LN.
- SETTLE the held invoice with P (the maker collects the BTC-LN).
The returned *ReverseResult always carries whatever was accomplished so the caller can drive the refund path (CancelHold + RefundReverseSEQ) on any error. The maker's SEQ refund PRIVATE key is not needed here — it is only used later, at CLTV time, via RefundReverseSEQ; RunReverse only needs the refund PUBKEY (p.MakerSeqRefundPub) to build the HTLC.
func (*SubmarineSwap) VerifySeqAnchorBuried ¶
func (m *SubmarineSwap) VerifySeqAnchorBuried(seqBlockHash string, minAnchorDepth int64) (*SubAnchorEvidence, error)
VerifySeqAnchorBuried is the submarine anchor-depth gate: it confirms the Sequentia block seqBlockHash is anchored, quorum-certified (if the node reports certification), and that its Bitcoin anchor is BURIED by at least minAnchorDepth Bitcoin blocks (node anchor tip - block anchorheight >= minAnchorDepth). This is the cross-leg safety point; minAnchorDepth MUST be > 1 for submarine swaps (1 is unsafe, design §3.2).
type Swap ¶
type Swap struct {
// contains filtered or unexported fields
}
Swap orchestrates a single Design-A cross-chain HTLC swap. It is written purely against LockPrimitive and the leg builders, so swapping in a PTLC primitive later requires no orchestration change.
The BTC (parent / anchor-source) leg is pluggable via btcBackend: it runs either against an Elements-mode parent (NewSwap) or a REAL bitcoind regtest/ testnet4 (NewSwapBitcoin). The SEQ (anchored Sequentia) leg is always Elements-format.
func NewSwap ¶
NewSwap wires an orchestrator to an ELEMENTS-mode parent (BTC leg) and the anchored Sequentia node (SEQ leg). This is the original constructor and the default for back-compat.
func NewSwapBitcoin ¶
func NewSwapBitcoin(btc *BitcoinChain, seq *Chain, prim *HashLock) *Swap
NewSwapBitcoin wires an orchestrator to a REAL bitcoind parent (BTC leg, in Bitcoin transaction format) and the anchored Sequentia node (SEQ leg, still Elements-format). This is the "real-bitcoind-leg": use it when the parent is a genuine bitcoind (regtest or testnet4), where the taker funds/refunds the BTC HTLC with a real Bitcoin signer and the maker must verify/claim it in Bitcoin format.
func (*Swap) ClaimBTCLeg ¶
ClaimBTCLeg performs step 5: Bob redeems the BTC leg with the now-revealed preimage. Returns the redeem txid. The spend is built in the BTC backend's transaction format (Elements or Bitcoin).
func (*Swap) ClaimSEQLeg ¶
ClaimSEQLeg performs step 4: Alice redeems the SEQ leg with the preimage, revealing it on-chain. Returns the redeem txid.
func (*Swap) InjectSecret ¶
InjectSecret fills in the preimage on a Swap's shared hashlock after the maker has read it off the taker's SEQ-leg claim. It verifies sha256(secret) == H before accepting it, so a malformed extraction can never produce a wrong-key claim. After this, Swap.ClaimBTCLeg can build the BTC redeem.
func (*Swap) LockBTCLeg ¶
func (s *Swap) LockBTCLeg(claimPub, refundPub []byte, amountCoins string, locktime uint32) (*LegLock, int64, error)
LockBTCLeg performs step 1: Alice locks the BTC leg first. Returns the funded leg and the parent height at which it confirmed (Hp), which the SEQ-leg ordering check is measured against.
func (*Swap) LockSEQLeg ¶
func (s *Swap) LockSEQLeg(claimPub, refundPub []byte, amountCoins, assetLabel string, locktime uint32) (*LegLock, string, error)
LockSEQLeg performs step 2: Bob locks the SEQ leg only after the BTC leg is on-chain (paper principle 7). It mines one Sequentia block and returns the funded leg plus the hash of the block that confirmed it (for the ordering check). The caller must then call VerifySeqLegSafe before treating it as safe.
func (*Swap) RefundBTCLeg ¶
func (s *Swap) RefundBTCLeg(leg *LegLock, makerRefund *Key, nLockTime uint32, fee uint64) (string, error)
RefundBTCLeg builds, broadcasts, and returns the txid of the BTC-leg CLTV refund (ELSE branch) at the given nLockTime. It is the reverse-direction (asset->BTC) mirror of RefundSEQLeg: the maker funds the BTC leg, so it must be able to reclaim that BTC after btcLocktime if the taker never funds/claims the SEQ leg. Delegates to the BTC backend (Elements or Bitcoin tx format).
func (*Swap) RefundSEQLeg ¶
func (s *Swap) RefundSEQLeg(leg *LegLock, bobRefund *Key, nLockTime uint32, fee uint64) (string, error)
RefundSEQLeg builds (but does not broadcast) the SEQ-leg CLTV refund for Bob, at the given nLockTime. Returns the raw tx hex so callers can demonstrate the pre-timeout rejection and the post-timeout acceptance.
func (*Swap) VerifyBTCLeg ¶
func (s *Swap) VerifyBTCLeg( hashH, makerClaimPub, takerRefundPub, providedScript []byte, btcLocktime uint32, txid string, vout uint32, amount uint64, assetID string, minConf int, ) (*VerifiedBTCLeg, error)
VerifyBTCLeg checks that the taker's claimed BTC leg really is a Design-A HTLC that pays the agreed amount, embeds the agreed hashlock H, is claimable by the MAKER's claim key and refundable by the TAKER's refund key after btcLocktime, is funded to the matching P2SH, and is confirmed. It returns a *LegLock the maker can feed to ClaimBTCLeg once it has the secret.
makerClaimPub is the maker's BTC-leg claim pubkey (from the quote); takerRefundPub is the taker's BTC-leg refund pubkey (from ProposeXchainSwap). providedScript is the redeemScript the taker sent; it must equal the script we recompute from (H, makerClaimPub, takerRefundPub, btcLocktime) byte-for-byte.
func (*Swap) VerifySEQLeg ¶
func (s *Swap) VerifySEQLeg( hashH, makerSeqClaimPub, takerSeqRefundPub, providedScript []byte, seqLocktime uint32, txid string, vout uint32, amount uint64, assetID string, minConf int, ) (*VerifiedSEQLeg, error)
VerifySEQLeg is the reverse-direction mirror of VerifyBTCLeg: it checks that the taker's funded SEQ asset leg really is a Design-A HTLC paying the agreed asset+amount, embedding the agreed hashlock H, claimable by the MAKER's seq-claim key and refundable by the TAKER's seq-refund key after seqLocktime, funded to the matching P2SH, and confirmed. It returns a *LegLock (inside VerifiedSEQLeg) the maker feeds to ClaimSEQLeg once VerifySeqLegSafe passes.
The SEQ leg is ALWAYS Elements-format (the anchored Sequentia node), so unlike VerifyBTCLeg this is not backend-pluggable; it runs directly against s.seqLeg + s.seq. makerSeqClaimPub is the maker's SEQ-leg claim pubkey (it generated H and will reveal the secret); takerSeqRefundPub is the taker's SEQ-leg refund pubkey; providedScript must equal the script we recompute byte-for-byte.
func (*Swap) VerifySeqLegSafe ¶
func (s *Swap) VerifySeqLegSafe(seqBlockHash string, btcLegHeight int64) (*AnchorEvidence, error)
VerifySeqLegSafe is the anchor-shortened ordering check (step 3): it confirms the Sequentia block carrying the SEQ leg anchors at a height >= the BTC-leg height AND that the node's anchor status is "ok". Returns ErrAnchorOrdering (wrapped) if not — the orchestrator must NOT let the SEQ-side claimant treat the leg as final unless this passes.
type VerifiedBTCLeg ¶
VerifiedBTCLeg is the maker's view of the taker's BTC-leg HTLC, reconstructed and checked so the maker can later claim it.
type VerifiedSEQLeg ¶
type VerifiedSEQLeg struct {
Leg *LegLock
Height int64
Confirmations int
BlockHash string
ExpectedScript []byte
}
WatchSEQClaim polls the SEQ chain for a spend of the SEQ-leg outpoint and, when found, extracts the preimage from its scriptSig. It returns the claim txid and the secret. It does NOT block forever; callers run it on a ticker.
We detect the claim by asking the node for spends of the funding outpoint via gettxout (nil => spent) and then scanning recent blocks/mempool for the spender; to keep the MVP self-contained we instead accept the claim txid the taker can be observed to have broadcast, so the daemon scans by re-deriving it from the funding tx's spend. See SpenderOf. VerifiedSEQLeg is the maker's view of the taker's SEQ asset-leg HTLC in the REVERSE (asset->BTC) direction, reconstructed and checked so the maker can later claim it (revealing the secret). BlockHash is the Sequentia block that confirmed the leg, fed to VerifySeqLegSafe for the anchor-ordering gate.