Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ through wasm.
- **`btc-blocking` cannot target wasm32** and says so with a `compile_error!`. Use `btc-async` for
anything that must run in a browser.
- Sequentia-specific modules exported from `lwk_wollet`: `btc`, `adaptor`, `openamp`, `ark`,
`seqdex_htlc`, `seqdex_swap`, `seqob_covenant`, `sequentia_delegation`, `coinjoin`, plus
`sequentia_stake_script` and the any-asset fee builder. `SEQUENTIA.md` documents each. `pos` is
`seqdex_htlc`, `seqdex_swap`, `seqob_covenant`, `sequentia_delegation`,
`sequentia_stake_records`, `coinjoin`, plus `sequentia_stake_script` and the any-asset fee
builder. `SEQUENTIA.md` documents each. `pos` is
upstream's point-of-sale module (feature `prices`), not proof of stake.

## Cross-language contracts
Expand Down
38 changes: 27 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,24 @@ on top of the upstream history, listed precisely in [SEQUENTIA.md](SEQUENTIA.md)
primitives (`bump_fee_of`, `replace_tx_of`, `cpfp_of`) work with any-asset fees.
- **Staking**: `sequentia_stake_script` / `TxBuilder::add_stake_output` build the
CSV-locked bonding output used to stake Sequence tokens for block production
(the only place the Sequence token is special).
(the only place the Sequence token is special), and `sequentia_stake_records`
takes a stake out in two steps (`build_unbond_tx`, then
`build_unbond_claim_tx` once the unbonding depth has passed). Every spend of
a stake record carries the signature the chain wants at the next block
(`StakeRecordSigning`): from the chain's `pos_records_v2_height`
(`pos_records_v2_height()` carries it per network) it commits to the amount
spent.
- **SeqDEX primitives**: the same-chain atomic-swap `SeqdexSwapRequest` builder
and the Sequentia-leg HTLC (redeem script, claim, refund) for cross-chain
BTC-to-asset swaps, byte-compatible with the SeqDEX daemon.
- **SeqOB covenant orders**: `seqob_covenant` assembles the raw FILL and REFUND
transactions for a resting passive-CLOB covenant order (a taproot script-path
input with no signature, plus the taker's own key-path funding inputs).
- **Staking-pool delegation**: `TxBuilder::add_delegation_output` creates a
delegation record and `sequentia_delegation` spends one (leave a pool, or
re-point to another signer in the same transaction).
- **Staking-pool delegation**: `sequentia_delegation` creates a delegation
record from a coin of the staking key (`TxBuilder::add_record_authorization`
pays it, `build_delegation_create_tx` spends it into the record, both mined
together) and spends one (leave a pool, or re-point to another signer in the
same transaction).
- **CoinJoin**: `coinjoin::sign_coinjoin_inputs` signs the wallet's own P2WPKH
inputs of a coordinator-built seqcj round transaction.
- **OpenAMP restricted assets** (feature `openamp`): AID derivation, the tagged
Expand Down Expand Up @@ -113,10 +121,10 @@ Sequentia example in `lwk_simplicity`.

| Crate | What it is |
|---|---|
| `lwk_wollet` | The watch-only wallet core (CT descriptors, scanning, balances, PSET create/finalize). Sequentia additions: explicit-output handling, any-asset fees + RBF/CPFP rescue, staking output, SeqDEX swap/HTLC builders, SeqOB covenant fill/refund, staking-pool delegation, CoinJoin input signing, the OpenAMP client (feature `openamp`), adaptor signatures (feature `adaptor`), the Arca leaf module (feature `ark`, `src/ark/`), and the whole Bitcoin parent-chain module (`src/btc/`). |
| `lwk_wollet` | The watch-only wallet core (CT descriptors, scanning, balances, PSET create/finalize). Sequentia additions: explicit-output handling, any-asset fees + RBF/CPFP rescue, staking output and two-step unbonding, SeqDEX swap/HTLC builders, SeqOB covenant fill/refund, staking-pool delegation, CoinJoin input signing, the OpenAMP client (feature `openamp`), adaptor signatures (feature `adaptor`), the Arca leaf module (feature `ark`, `src/ark/`), and the whole Bitcoin parent-chain module (`src/btc/`). |
| `lwk_common` | Shared types. Sequentia addition: `Network::sequentia_testnet()` and Sequentia address parameters. |
| `lwk_signer` | Software signer (BIP39 mnemonic to PSET signatures); signs Sequentia PSETs as-is. Sequentia additions: `sign_tapscript`, a script-path signature at any leaf version over the Elements signature hash, for covenant leaves whose signature opcodes check the wallet's key, under `SIGHASH_DEFAULT` or `SIGHASH_ALL` unless the caller names another type; `sign_csfs`, a message signature for `OP_CHECKSIGFROMSTACK` over Arca's three messages, which rebuilds the digest from the message's fields, refuses a mismatch or another chain than the wallet's, and caps what a rebind leaves to whoever broadcasts. Each has a `describe` for the approval screen. |
| `lwk_wasm` | WebAssembly bindings (wasm-bindgen). Sequentia additions: `Network.sequentiaTestnet()`, `BtcWallet`, the `xchain*` HTLC helpers, SeqDEX bindings, `buildCovenantFillTx` / `buildCovenantRefundTx`, delegation (`buildDelegationSpendTx`, `findDelegationRecords`), `coinjoinSignInputs` / `coinjoinUnblindOutputs`, the `Openamp` client and enclave helpers, `adaptor*`, `Signer.signTapscript` / `tapscriptSighash` / `tapscriptDescribe`, `Signer.signCsfs` / `csfsDigest` / `csfsDescribe`, Arca leaves (`ArkVerifier`, `ArkStore`, `arkParseRecord`, `Signer.arkLeafKey`), staking and any-asset-fee bindings. |
| `lwk_wasm` | WebAssembly bindings (wasm-bindgen). Sequentia additions: `Network.sequentiaTestnet()`, `BtcWallet`, the `xchain*` HTLC helpers, SeqDEX bindings, `buildCovenantFillTx` / `buildCovenantRefundTx`, delegation (`buildDelegationCreateTx`, `buildDelegationSpendTx`, `findDelegationRecords`), unbonding (`buildUnbondTx`, `buildUnbondClaimTx`), `coinjoinSignInputs` / `coinjoinUnblindOutputs`, the `Openamp` client and enclave helpers, `adaptor*`, `Signer.signTapscript` / `tapscriptSighash` / `tapscriptDescribe`, `Signer.signCsfs` / `csfsDigest` / `csfsDescribe`, Arca leaves (`ArkVerifier`, `ArkStore`, `arkParseRecord`, `Signer.arkLeafKey`), staking and any-asset-fee bindings. |
| `lwk_bindings` | UniFFI bindings (Python, Kotlin, Swift, C#, Go, C++). Upstream API surface (no Sequentia network exposed yet); only the `Contract::from_parts` call changed. |
| `lwk_cli` / `lwk_app` / `lwk_rpc_model` / `lwk_tiny_jrpc` | JSON-RPC wallet server and CLI client. Upstream apart from `lwk_app`'s `Contract::from_parts` call: no `sequentia` network selector yet (networks: liquid, liquid-testnet, regtest). |
| `lwk_jade`, `lwk_ledger`, `lwk_hwi` | Hardware-signer support (upstream; not wired to Sequentia flows). |
Expand Down Expand Up @@ -170,9 +178,10 @@ lwk_wollet = { features = ["btc-async"] } # wasm / async apps
`Network.sequentiaTestnet()`, `Network.isSequentia()`, the dual-chain
`BtcWallet`, the `xchain*` helpers for cross-chain swaps,
`TxBuilder.feeAsset()` / `addStakeOutput()` / `addExplicitRecipient()` /
`addDelegationOutput()`, `Signer.stakerPublicKey()`, `buildCovenantFillTx()`,
`buildDelegationSpendTx()`, `coinjoinSignInputs()`, the `Openamp` client, and
the `adaptor*` functions.
`addRecordAuthorization()`, `Signer.stakerPublicKey()`, `buildCovenantFillTx()`,
`buildDelegationCreateTx()` / `buildDelegationSpendTx()`, `buildUnbondTx()` /
`buildUnbondClaimTx()`, `coinjoinSignInputs()`, the `Openamp` client, and the
`adaptor*` functions.

```sh
cd lwk_wasm
Expand All @@ -192,6 +201,7 @@ Standard Rust workspace (toolchain pinned in `rust-toolchain.toml`):
cargo build # whole workspace
cargo test -p lwk_wollet --lib --features btc-blocking,btc-async btc # dual-chain tests
cargo test -p lwk_wollet --lib --features sequentia seqdex # SeqDEX builders
cargo test -p lwk_wollet --lib --features sequentia sequentia_ # staking records
```

Notes:
Expand All @@ -201,8 +211,14 @@ Notes:
failing upstream fixture tests. The workspace enables the vendored `elements`
crate's `sequentia` feature globally, which changes the transaction and
header wire format, so upstream Liquid test vectors no longer deserialize.
The Sequentia-specific test modules (`btc`, `seqdex_htlc`, `seqdex_swap`) all
pass; use the filtered commands above.
The Sequentia-specific test modules (`btc`, `seqdex_htlc`, `seqdex_swap`,
`sequentia_delegation`, `sequentia_stake_records`) all pass; use the filtered
commands above.
- `lwk_wollet/tests/sequentia_stake_records.rs` has a proof-of-stake
`sequentiad` on a regtest chain confirm every staking transaction the kit
builds, and refuse each wrong one in the mempool and in a block:
`SEQUENTIAD_EXEC=/path/to/sequentiad cargo test -p lwk_wollet --features
sequentia --test sequentia_stake_records`.
- Upstream integration tests (`lwk_wollet/tests/e2e.rs` and friends) need a
local Elements/Liquid test environment (see `lwk_test_util` and
`lwk_containers`); they exercise upstream Liquid behavior, not Sequentia.
Expand Down
75 changes: 62 additions & 13 deletions SEQUENTIA.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,43 @@ Changes by file:
`sequentia_stake_script()` and `TxBuilder::add_stake_output(staker_pubkey,
csv, satoshi)` build the CSV-locked bonding output used to stake for block
production.
- Staking pools: `TxBuilder::add_delegation_output()` pays the bare
- Staking pools: `TxBuilder::add_record_authorization(pubkey, satoshi)`
pays the staking key's `P2WPKH`, the coin that authorises a delegation
record (below). `TxBuilder::add_delegation_output()` pays the bare
delegation-record script (`"SEQDEL" OP_DROP <signer> OP_DROP <controller>
OP_CHECKSIG`) that lends the wallet's stake weight to a pool signer.
- `src/sequentia_delegation.rs` (feature `sequentia`): spends a delegation
record, which no descriptor matches. `build_delegation_spend_tx()` either
reclaims the record (leave the pool) or re-points it to another signer in
the same transaction (consensus allows one live record per controller, so
the two steps must not be separate transactions). Also
OP_CHECKSIG`) straight from wallet coins, which the network accepts only
below `pos_hardening_height` (on the testnet the same height as its
`pos_records_v2_height`; block 1 on every other chain).
- `src/sequentia_delegation.rs` (feature `sequentia`): creates and spends
delegation records, which no descriptor matches. A record must be created
by a transaction spending a coin of its controller, so
`build_delegation_create_tx()` funds it from such a coin and nothing else:
the wallet pays the staking key's `P2WPKH` first
(`add_record_authorization`), and the two transactions are mined together.
`build_delegation_spend_tx()` either reclaims the record (leave the pool) or
re-points it to another signer in the same transaction (consensus allows one
live record per controller, so the two steps must not be separate
transactions; the spent record authorises the new one). Also
`sequentia_delegation_script()` and `parse_delegation_script()`.
- `src/sequentia_stake_records.rs` (feature `sequentia`): stake record spends
(staking, unbonding, delegation and payout outputs) and two-step unbonding.
`StakeRecordSigning` names the signature a spend needs in the block after
the wallet's tip: the legacy hash below the chain's `pos_records_v2_height`,
and from it the segwit-v0 hash over the record script, committing to the
amount, in a scriptSig of one minimal low-S push. `pos_records_v2_height()`
is `SEQUENTIA_TESTNET_POS_RECORDS_V2_HEIGHT` for
`Network::sequentia_testnet()` (matched on its whole definition, genesis
hash included) and 1 on every other chain; no node RPC reports it.
`sign_stake_record_input()` signs any record spend that way.
`build_unbond_tx()` moves staking outputs into an unbonding output of the
same key (`sequentia_unbond_script()`, `"SEQUNBOND" OP_DROP <key>
OP_CHECKSIG`), its fee capped at 1% of the stake (`unbond_fee_cap()`);
`build_unbond_claim_tx()` sends unbonding outputs to an address once the
unbonding depth has passed. `build_record_create_tx()` creates any record
from a `P2WPKH` coin of its key; the kit announces no payout policy of its
own (that belongs to the node wallet), but its tests create and withdraw one
this way. Also `parse_stake_script()`, `key_coin_script()` and
`find_key_coins()`.
- `src/seqob_covenant.rs` (feature `sequentia`): the raw-Elements FILL and
REFUND transaction assemblers for a resting SeqOB passive-CLOB covenant
order (`build_covenant_fill_tx()`, `build_covenant_refund_tx()`). The
Expand Down Expand Up @@ -406,6 +434,18 @@ Changes by file:
CLTV timelocks are liveness only.
- `examples/sequentia_sync.rs`: end-to-end watch-only sync against the live
explorer (`cargo run -p lwk_wollet --example sequentia_sync`).
- `tests/sequentia_stake_records.rs`: every stake record transaction the kit
builds, confirmed in a block a proof-of-stake `sequentiad` produces on an
`elementsregtest` chain, under the node's default relay policy: a bond, a
delegation created with the controller's coin, a re-point, a reclaim, a
payout announcement and its withdrawal, and an unbond in two steps; then a
chain crossing `pos_records_v2_height` at block 12, signed the legacy way
below it and the second-generation way at it. Each wrong case (a record from
wallet coins alone, a spend signed for the other generation, a claim before
the unbonding depth) is refused by the mempool and by a block
(`testproposedblock` on the node's own next block with the transaction
added), which accepts the right one. It needs `SEQUENTIAD_EXEC`, and runs
`lwk_wasm/tests/node/stake_records.js` when the node package is linked.
- `examples/rescue_test.rs`: live functional test of bump/replace/CPFP against
the testnet (needs a funded wallet).

Expand All @@ -429,7 +469,8 @@ The fork is not published to npm; consumers build `pkg/` with `wasm-pack`.
- `src/seqdex_htlc.rs`: `generateSwapSecret`, `htlcKeypair`,
`buildSeqHtlcRedeemScript`, `buildSeqHtlcClaimTx`, `buildSeqHtlcRefundTx`.
- `src/tx_builder.rs`: `feeAsset()` (any-asset fees), `addExplicitRecipient()`,
`addStakeOutput()`, `sequentiaStakeScript()`, `addDelegationOutput()`.
`addStakeOutput()`, `sequentiaStakeScript()`, `addRecordAuthorization()`,
`addDelegationOutput()`.
- `src/wollet.rs`: explicit-UTXO and rescue (bump/replace/CPFP) bindings.
- `src/network.rs`: `Network.regtestWithGenesis(policyAsset, genesisHash)`, a
regtest network for a chain started with its own parameters, whose genesis
Expand Down Expand Up @@ -535,7 +576,15 @@ The fork is not published to npm; consumers build `pkg/` with `wasm-pack`.
- `src/seqob_covenant.rs`: `buildCovenantFillTx`, `buildCovenantRefundTx`,
`covenantMakerAddress`, `covenantMakerDescriptor`, `scriptToAddress`.
- `src/sequentia_delegation.rs`: `sequentiaDelegationScript`,
`parseDelegationScript`, `findDelegationRecords`, `buildDelegationSpendTx`.
`parseDelegationScript`, `findDelegationRecords`, `buildDelegationCreateTx`,
`buildDelegationSpendTx`, `stakeRecordSigning`. The builders take the chain
tip (`tipHeight`, defaulting to `locktime`) and sign for the block after it.
- `src/sequentia_stake_records.rs`: `buildUnbondTx` and `buildUnbondClaimTx`
(the two steps of unbonding), `sequentiaUnbondScript`, `unbondFeeCap`.
- `tests/node/stake_records.js`: the wasm half of
`lwk_wollet/tests/sequentia_stake_records.rs`: every recipe the test's
confirmed transactions came from, through the bindings, must give the same
transaction byte for byte.
- `src/coinjoin.rs`: `coinjoinSignInputs`, `coinjoinUnblindOutputs`.
- `src/openamp.rs`: the `Openamp` client class (`registerUser`, `getUser`,
`enclaveAddress`, `assetInfo`, `createTransfer`, `completeTransfer`) and
Expand All @@ -556,7 +605,7 @@ purposes (BIP44 `44'`, BIP49 `49'`, BIP84 `84'`, BIP86 `86'`, AMP2 `87'`):

| Path | Key |
|---|---|
| `m/2/0` | The staking key: stake bonding, staking-pool delegation, and messages signed as the staker |
| `m/2/0` | The staking key: stake bonding and unbonding, staking-pool delegation (with the `P2WPKH` coin that authorises a record), and messages signed as the staker |
| `m/3/0` | The SeqDEX HTLC key (`htlcKeypair`), and the cross-chain swap's Sequentia claim key in its legacy relative mode; the cross-chain swap's own keys are at `m/84'/1'/0'/2/0` (BTC refund), `/3/0` (Sequentia claim) and `/4/0` (BTC claim) |
| `m/5/0` | The OpenAMP enclave key, which signs raw 32-byte digests (`openampSignSighash`) |
| `m/6'/<account>'/c1'/c2'/c3'/c4'` | Arca leaf keys, one per leaf, from the leaf's owner nonce (`ark::keys`) |
Expand All @@ -570,9 +619,9 @@ shares a path with one that signs anything else.
- Sequentia is transparent by default; confidentiality is opt-in. Docs and code
never assume blinded-by-default (that is the Liquid model).
- The Sequence token (SEQ; tSEQ on testnet) is the policy asset but has no
privilege anywhere in the kit except the staking output builder; fees are
payable in any accepted asset and fee rates are denominated in the chosen
asset's own units per vByte.
privilege anywhere in the kit except staking (bonding, unbonding and
delegation records); fees are payable in any accepted asset and fee rates
are denominated in the chosen asset's own units per vByte.
- One redeemScript source for cross-chain HTLCs: the Bitcoin leg wraps the same
builder the Sequentia leg uses, so the legs cannot drift (this is why the
`btc` feature implies `sequentia`).
Expand Down
22 changes: 20 additions & 2 deletions lwk_wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
> and `Network.isSequentia()`, the Bitcoin testnet4 `BtcWallet` (dual-chain
> wallets), the `xchain*` helpers (cross-chain BTC-to-asset HTLC swaps), SeqDEX
> bindings (`SwapRequest`, `buildSeqHtlc*`), `TxBuilder.feeAsset()` (any-asset
> fees), `addStakeOutput()` and `Signer.stakerPublicKey()` (staking),
> `addDelegationOutput()` / `buildDelegationSpendTx()` (staking pools),
> fees), `addStakeOutput()`, `buildUnbondTx()` / `buildUnbondClaimTx()` and
> `Signer.stakerPublicKey()` (staking), `addRecordAuthorization()` /
> `buildDelegationCreateTx()` / `buildDelegationSpendTx()` (staking pools),
> `buildCovenantFillTx()` / `buildCovenantRefundTx()` (SeqOB covenant orders),
> `coinjoinSignInputs()` / `coinjoinUnblindOutputs()` (CoinJoin), the `Openamp`
> client and `adaptor*` functions. See
Expand Down Expand Up @@ -155,6 +156,23 @@ SEQUENTIAD_EXEC=/path/to/sequentiad cargo test -p lwk_wollet \
--no-default-features --features ark --test ark_regtest -- --nocapture
```

### Stake records on regtest

`lwk_wollet/tests/sequentia_stake_records.rs` has a proof-of-stake
`sequentiad` confirm every stake record transaction the kit builds (a bond, a
delegation created with the staking key's coin, a re-point, a reclaim, an
unbond in two steps) on both sides of the chain's `pos_records_v2_height`, and
then runs `tests/node/stake_records.js`, which builds each of them again
through the bindings from the recipe a wallet would pass and must get the same
transaction byte for byte. The wasm half needs the node package above, linked
or installed as `lwk_node` in `tests/node/node_modules`, and is skipped, with a
line saying so, without it:

```shell
SEQUENTIAD_EXEC=/path/to/sequentiad cargo test -p lwk_wollet \
--features sequentia --test sequentia_stake_records -- --nocapture
```

### Arca signers on regtest

`tests/node/scripts/arca_regtest.py` spends the Arca reference scripts on a
Expand Down
Loading
Loading