waved: Operate native Ark channels - #1192
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Ark channel daemon support and OOR recovery/export surfaces to waved/wavecli, wiring new gRPC + REST endpoints, durable channel lifecycle control, and on-chain recovery/watch infrastructure.
Changes:
- Extend
waverpc.DaemonServicewith Ark-channel OOR preparation/commit/abort RPCs and an OOR recovery-package export RPC (plus REST gateway + mailbox + REST client wiring). - Introduce Ark channel runtime/process wiring in
waved(controller startup/shutdown, source archive + watcher, pre-PONR expiry maintenance) with accompanying tests. - Add a hidden
wavecli channelsubtree and update CLI docs and dev-RPC registry.
Reviewed changes
Copilot reviewed 33 out of 35 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| waverpc/daemon.yaml | Add HTTP REST mappings for new daemon RPCs |
| waverpc/daemon.proto | Add new daemon RPCs/messages; add identity_key to NewReceiveScriptRequest |
| waverpc/daemon.pb.gw.go | Regenerated gateway handlers for new endpoints |
| waverpc/daemon_mailboxrpc.pb.go | Regenerated mailbox RPC stubs for new methods |
| waverpc/daemon_grpc.pb.go | Regenerated gRPC stubs for new methods |
| waved/vhtlc_recovery_target.go | Persist ConstructionVersion in recovery descriptors |
| waved/server.go | Wire ArkChannelService registration + runtime lifecycle/shutdown hooks |
| waved/rpc_oor_receive.go | Support identity-key-backed receive script registration |
| waved/rpc_auth.go | Add macaroon entity/permissions for channel RPCs |
| waved/rpc_auth_test.go | Extend permissions test coverage for new RPC surface |
| waved/rpc_ark_channel_recovery.go | Add daemon RPC to export local OOR recovery package |
| waved/rpc_ark_channel_oor.go | Add daemon RPCs for preparing/validating/committing/aborting Ark-channel OOR |
| waved/native_ark_channel_controller_test.go | Add tests for channel restore/watch/resume helpers |
| waved/logging.go | Register additional lnd subsystems with log manager |
| waved/config.go | Add process-wiring mailbox client for channel runtime |
| waved/ark_channel_source.go | Implement channel recovery archive + lineage verification + descriptor reconstruction |
| waved/ark_channel_source_watcher.go | Add passive chain-spend watcher that retries durable channel events |
| waved/ark_channel_source_watcher_test.go | Tests for watcher coverage and retry behavior |
| waved/ark_channel_source_test.go | Tests for recovery roots/descriptor/operator-key handling |
| waved/ark_channel_recovery_runtime.go | Add standalone recovery runtime composition (unroll + watches) |
| waved/ark_channel_process.go | Add Ark channel process wiring + ArkChannelService gRPC server |
| waved/ark_channel_process_test.go | Tests for ArkChannelService RPC behavior/unavailable states |
| waved/ark_channel_oor.go | Implement wallet selection + OOR preparation + reconciliation cleanup |
| waved/ark_channel_expiry.go | Add pre-PONR maintenance loop to expire/abort abandoned preparations |
| waved/ark_channel_expiry_test.go | Tests for pre-PONR expiry/abort behavior |
| rpc/restclient/clients.go | Add REST client methods for new daemon endpoints |
| harness/harness.go | Adjust channel setup parameters (explicit Private: false) |
| docs/daemon_cli_guide.md | Document new hidden channel CLI subtree and behavior |
| cmd/wavecli/waveclicommands/root.go | Register channel subtree under advanced commands |
| cmd/wavecli/waveclicommands/root_groups_test.go | Update tests for advanced command visibility/grouping |
| cmd/wavecli/waveclicommands/devrpc/registry_generated.go | Regenerate dev-rpc registry for new daemon methods |
| cmd/wavecli/waveclicommands/cmd_channel.go | Add wavecli channel commands and channel-id parsing |
| cmd/wavecli/waveclicommands/cmd_channel_test.go | Tests for channel-id and amount parsing |
Files not reviewed (1)
- cmd/wavecli/waveclicommands/devrpc/registry_generated.go: Generated file
Suppressed comments (2)
waved/ark_channel_oor.go:211
- When PrepareChannel reports Existing=true, the selected VTXOs should be unlocked even if the request ctx is already canceled. Using ctx here can prevent the unlock Tell from being delivered, leaving locks behind.
if prepared.Existing {
rpcServer.unlockSelectedVTXOsBestEffort(ctx, locked)
}
waved/rpc_ark_channel_oor.go:62
- This gRPC handler returns a plain fmt.Errorf for invalid request terms, which maps to codes.Unknown. Prefer returning codes.InvalidArgument via status.Error/status.Errorf for consistent client-visible semantics.
if terms.Funder != arkchannel.PartyHub ||
terms.Kind != arkchannel.KindReceiveIntent {
return nil, fmt.Errorf("daemon channel OOR must fund a " +
"receive intent")
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| fail := func(err error) (arkchannel.VTXOBinding, error) { | ||
| rpcServer.unlockSelectedVTXOsBestEffort(ctx, locked) | ||
|
|
||
| return arkchannel.VTXOBinding{}, err | ||
| } |
| defer func() { | ||
| if runtime := s.getArkChannelMailboxRuntime(); runtime != nil { | ||
| shutdownCtx, cancel := context.WithTimeout( | ||
| context.Background(), DefaultShutdownTimeout, | ||
| ) | ||
| defer cancel() | ||
|
|
||
| _ = runtime.StopAndWait(shutdownCtx) | ||
| } | ||
| }() |
| if r == nil || r.server == nil || r.server.vtxoStore == nil { | ||
| return nil, fmt.Errorf("OOR recovery exporter is unavailable") | ||
| } |
| if terms.Funder != arkchannel.PartyHub || | ||
| terms.Kind != arkchannel.KindReceiveIntent { | ||
| return nil, fmt.Errorf("daemon channel OOR must fund a " + | ||
| "receive intent") | ||
| } |
| return nil, fmt.Errorf("unknown channel OOR preparation "+ | ||
| "status %d", lookup.Status) | ||
| } |
| if req.GetReason() == "" { | ||
| return nil, fmt.Errorf("channel OOR abort reason is required") | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 070cf3dfa4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if s.cfg.Swap == nil || s.cfg.Swap.ArkChannelMailbox == nil { | ||
| return nil |
There was a problem hiding this comment.
Publish the channel mailbox before actor startup
initArkChannelProcess always takes this early return in the shipped daemon: a repo-wide search finds no assignment to SwapConfig.ArkChannelMailbox, while swapclientserver.Register only publishes Backend, CreditServer, and CreditDaemon in swapclientserver/service.go:304-315. Consequently no channel controller is constructed and every newly exposed wavecli channel RPC returns Unavailable; publish swapClients.mailbox into the config before wallet-dependent actors start.
Useful? React with 👍 / 👎.
| } | ||
| rpcServer := &RPCServer{server: s} | ||
| fail := func(err error) (arkchannel.VTXOBinding, error) { | ||
| rpcServer.unlockSelectedVTXOsBestEffort(ctx, locked) |
There was a problem hiding this comment.
Use a live bounded context for VTXO unlocks
If the request is canceled after wallet selection—such as while BuildTransferInputs, change construction, or OOR preparation is running—the failure path passes that canceled context to Tell, so UnlockVTXOsRequest is rejected and the selected coins remain locked. Detach this cleanup from the request and bound it with a fresh timeout, as the existing submitted-OOR cleanup path does.
AGENTS.md reference: AGENTS.md:L77-L81
Useful? React with 👍 / 👎.
| record, err := c.service.GetChannel(ctx, preparation.ChannelID) | ||
| if err != nil { | ||
| return LightningPaymentResult{}, err |
There was a problem hiding this comment.
Cancel the remote preparation when local lookup fails
Once PrepareOutgoingPayment succeeds, the operator owns a durable hold/payment worker, but a transient local GetChannel failure returns here without calling CancelOutgoingPayment; the SCID-mismatch return has the same problem. The only cancellation currently occurs for the later native payment error, so these earlier failures leave the remote preparation active until its own expiry or recovery; arrange cleanup for every failure after preparation.
Useful? React with 👍 / 👎.
| cancelErr := c.paymentPeer.CancelOutgoingPayment( | ||
| ctx, preparation.PaymentHash, err.Error(), | ||
| ) |
There was a problem hiding this comment.
Detach outgoing-payment cancellation from the request
When PayInvoiceResult fails because the RPC deadline expired or its caller disconnected, ctx is already canceled, so this mailbox cancellation fails immediately and cannot release the operator's hold/payment worker. Run this required cleanup with a fresh bounded context that survives request cancellation.
AGENTS.md reference: AGENTS.md:L77-L81
Useful? React with 👍 / 👎.
| var keyDesc *keychain.KeyDescriptor | ||
| var pkScript []byte | ||
| if req.GetIdentityKey() { | ||
| identityKey := r.server.clientKeyDesc |
There was a problem hiding this comment.
Read the identity descriptor through its lock
For an identity_key request, this accesses clientKeyDesc directly rather than through loadClientKeyDesc, so any overlap with startup publication creates an unsynchronized read/write and may observe an incomplete descriptor. Use the established accessor for this new RPC path.
AGENTS.md reference: waved/AGENTS.md:L284-L290
Useful? React with 👍 / 👎.
Doc gardening: scoped drift advisoryThis PR's Go changes leave the per-package docs for
Proposed diff — 354 insertions, 24 deletions across 8 filesdiff --git a/cmd/wavecli/waveclicommands/AGENTS.md b/cmd/wavecli/waveclicommands/AGENTS.md
index fa9b16a5..6435fd27 100644
--- a/cmd/wavecli/waveclicommands/AGENTS.md
+++ b/cmd/wavecli/waveclicommands/AGENTS.md
@@ -16,15 +16,16 @@ default `--help` face:
wavewalletrpc-backed.
2. **Daemon introspection (group "Introspection")** — getinfo, schema, mcp
(the built-in `help` command is grouped here too).
-3. **Advanced subtrees (`ark`, `dev`, `recovery`)** — raw
- waverpc/devrpc commands for power users and operator runbooks.
+3. **Advanced subtrees (`ark`, `channel`, `dev`, `recovery`)** — raw
+ waverpc/arkchannelrpc/devrpc commands for power users and operator
+ runbooks.
Hidden from the default `--help` via cobra `Hidden` (not a build tag),
so they stay compiled and fully runnable in the shipped binary;
`WAVELENGTH_DEV=1` reveals them under an "Advanced" group. The env var only
changes visibility — it never gates execution. `ark.*` stays on the
`schema`/MCP surfaces; `dev` is reachable only as the generated (hidden)
- CLI subtree — it is not registered in `schema`/MCP — and `recovery` is
- exposed on neither.
+ CLI subtree — it is not registered in `schema`/MCP — and `channel` and
+ `recovery` are exposed on neither.
The `swap.*` verbs were retired: `send`/`recv --offchain` and `activity`
cover them (the swapruntime daemon runtime that powers those verbs is
@@ -72,6 +73,22 @@ who want direct access.
| `ark listtransactions` | `ListTransactions` | Raw paginated transaction history |
| `ark send {inround,oor}` | `SendVTXO` / `SendOOR` | Raw in-round / OOR send; real transfers require interactive approval or `--yes` |
+### `channel.*` advanced commands
+
+Development control surface for native Ark-backed Lightning channels,
+served by `arkchannelrpc.ArkChannelService` on the same daemon listener.
+Every verb prints the raw proto JSON response.
+
+| Command | RPC | Description |
+|---------|-----|-------------|
+| `channel create <amount-sat>` | `PromoteVTXO` | Promote wallet value into a channel; capacity is the only input — the daemon owns OOR preparation, backing reserve, and activation |
+| `channel get <channel-id>` | `GetChannel` | One durable channel snapshot |
+| `channel send <channel-id> <amount-sat>` | `SendPayment` | Pay a hub invoice over one active channel |
+| `channel receive <channel-id> <amount-sat>` | `ReceivePayment` | Create and settle a client invoice over one active channel |
+| `channel pay <bolt11>` | `PayLightningInvoice` | Bridge a private HTLC into the operator's public Lightning payment; `--max-fee-sat` (default 100000) caps routing fees |
+| `channel close <channel-id>` | `RequestCooperativeClose` | Start or resume the client-owned cooperative close |
+| `channel force-close <channel-id>` | `MaterializeAndForceClose` | Publish the Ark ancestry, signed backing, and latest commitment |
+
### `recovery.*` advanced commands
Manual control of daemon-owned vHTLC recovery rows; normal swap clients
@@ -136,6 +153,8 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/cmd/wave
- `rpc/wavewalletrpc` (generated stubs for the top-level wallet verbs;
`WalletService` / `WalletInspectionService` clients).
- `waverpc` (generated stubs for `ark.*`, `recovery.*`, getinfo).
+ - `rpc/arkchannelrpc` (generated `ArkChannelService` stubs for
+ `channel.*`).
- `cmd/wavecli/waveclicommands/devrpc` (the generated `dev`
subtree; its registry references `swapclientrpc` service
descriptors).
@@ -182,6 +201,18 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/cmd/wave
which flag to drop and why, since stating the constraint alone invites
dropping `--onchain-address` and silently getting the more dangerous
unilateral exit (`swapwallet.forceUnroll` carries the same wording).
+- `channel.*` dials `ArkChannelService` through the same
+ `getDaemonConn` transport as every other verb, via the
+ `getArkChannelClient` seam that command-wiring tests replace.
+ `parseChannelID` accepts a channel ID as either 32-byte hex or the
+ base64 form protobuf JSON prints, so an ID copied straight out of a
+ previous command's output round-trips; anything that does not decode to
+ exactly 32 bytes is rejected. Amounts must parse as strictly positive
+ int64.
+- `channel.*` verbs are NOT confirmation-gated and are NOT two-phase —
+ `create`, `send`, `pay`, `close`, and `force-close` all move funds in a
+ single call. They stay off the `schema`/MCP surfaces for that reason;
+ the gate is that the subtree is development-only.
- `recovery escalate` refuses to run on non-interactive stdin unless
`--yes` is passed — it never blocks on a y/N prompt an agent can't
answer.
diff --git a/cmd/wavecli/waveclicommands/CLAUDE.md b/cmd/wavecli/waveclicommands/CLAUDE.md
index fa9b16a5..6435fd27 100644
--- a/cmd/wavecli/waveclicommands/CLAUDE.md
+++ b/cmd/wavecli/waveclicommands/CLAUDE.md
@@ -16,15 +16,16 @@ default `--help` face:
wavewalletrpc-backed.
2. **Daemon introspection (group "Introspection")** — getinfo, schema, mcp
(the built-in `help` command is grouped here too).
-3. **Advanced subtrees (`ark`, `dev`, `recovery`)** — raw
- waverpc/devrpc commands for power users and operator runbooks.
+3. **Advanced subtrees (`ark`, `channel`, `dev`, `recovery`)** — raw
+ waverpc/arkchannelrpc/devrpc commands for power users and operator
+ runbooks.
Hidden from the default `--help` via cobra `Hidden` (not a build tag),
so they stay compiled and fully runnable in the shipped binary;
`WAVELENGTH_DEV=1` reveals them under an "Advanced" group. The env var only
changes visibility — it never gates execution. `ark.*` stays on the
`schema`/MCP surfaces; `dev` is reachable only as the generated (hidden)
- CLI subtree — it is not registered in `schema`/MCP — and `recovery` is
- exposed on neither.
+ CLI subtree — it is not registered in `schema`/MCP — and `channel` and
+ `recovery` are exposed on neither.
The `swap.*` verbs were retired: `send`/`recv --offchain` and `activity`
cover them (the swapruntime daemon runtime that powers those verbs is
@@ -72,6 +73,22 @@ who want direct access.
| `ark listtransactions` | `ListTransactions` | Raw paginated transaction history |
| `ark send {inround,oor}` | `SendVTXO` / `SendOOR` | Raw in-round / OOR send; real transfers require interactive approval or `--yes` |
+### `channel.*` advanced commands
+
+Development control surface for native Ark-backed Lightning channels,
+served by `arkchannelrpc.ArkChannelService` on the same daemon listener.
+Every verb prints the raw proto JSON response.
+
+| Command | RPC | Description |
+|---------|-----|-------------|
+| `channel create <amount-sat>` | `PromoteVTXO` | Promote wallet value into a channel; capacity is the only input — the daemon owns OOR preparation, backing reserve, and activation |
+| `channel get <channel-id>` | `GetChannel` | One durable channel snapshot |
+| `channel send <channel-id> <amount-sat>` | `SendPayment` | Pay a hub invoice over one active channel |
+| `channel receive <channel-id> <amount-sat>` | `ReceivePayment` | Create and settle a client invoice over one active channel |
+| `channel pay <bolt11>` | `PayLightningInvoice` | Bridge a private HTLC into the operator's public Lightning payment; `--max-fee-sat` (default 100000) caps routing fees |
+| `channel close <channel-id>` | `RequestCooperativeClose` | Start or resume the client-owned cooperative close |
+| `channel force-close <channel-id>` | `MaterializeAndForceClose` | Publish the Ark ancestry, signed backing, and latest commitment |
+
### `recovery.*` advanced commands
Manual control of daemon-owned vHTLC recovery rows; normal swap clients
@@ -136,6 +153,8 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/cmd/wave
- `rpc/wavewalletrpc` (generated stubs for the top-level wallet verbs;
`WalletService` / `WalletInspectionService` clients).
- `waverpc` (generated stubs for `ark.*`, `recovery.*`, getinfo).
+ - `rpc/arkchannelrpc` (generated `ArkChannelService` stubs for
+ `channel.*`).
- `cmd/wavecli/waveclicommands/devrpc` (the generated `dev`
subtree; its registry references `swapclientrpc` service
descriptors).
@@ -182,6 +201,18 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/cmd/wave
which flag to drop and why, since stating the constraint alone invites
dropping `--onchain-address` and silently getting the more dangerous
unilateral exit (`swapwallet.forceUnroll` carries the same wording).
+- `channel.*` dials `ArkChannelService` through the same
+ `getDaemonConn` transport as every other verb, via the
+ `getArkChannelClient` seam that command-wiring tests replace.
+ `parseChannelID` accepts a channel ID as either 32-byte hex or the
+ base64 form protobuf JSON prints, so an ID copied straight out of a
+ previous command's output round-trips; anything that does not decode to
+ exactly 32 bytes is rejected. Amounts must parse as strictly positive
+ int64.
+- `channel.*` verbs are NOT confirmation-gated and are NOT two-phase —
+ `create`, `send`, `pay`, `close`, and `force-close` all move funds in a
+ single call. They stay off the `schema`/MCP surfaces for that reason;
+ the gate is that the subtree is development-only.
- `recovery escalate` refuses to run on non-interactive stdin unless
`--yes` is passed — it never blocks on a y/N prompt an agent can't
answer.
diff --git a/harness/AGENTS.md b/harness/AGENTS.md
index 2ac7ee2f..6be2bd71 100644
--- a/harness/AGENTS.md
+++ b/harness/AGENTS.md
@@ -48,6 +48,11 @@ helpers for end-to-end tests.
primary LND node resyncs to the new tip.
- `LNDRequireInterceptor` only applies to the primary LND node; additional
nodes started via `StartAdditionalLND*` never set it.
+- `SetupChannelBetween` opens **announced** channels (`Private: false`,
+ set explicitly rather than left to the proto zero value). Tests that
+ route through a third node need the channel in the network graph, and
+ relying on the default would silently flip them to private if lnd ever
+ changed it.
- Container teardown (`Stop`) is guarded by `sync.Once`; a signal handler
also calls `Stop` as a safety net against orphaned containers.
diff --git a/harness/CLAUDE.md b/harness/CLAUDE.md
index 2ac7ee2f..6be2bd71 100644
--- a/harness/CLAUDE.md
+++ b/harness/CLAUDE.md
@@ -48,6 +48,11 @@ helpers for end-to-end tests.
primary LND node resyncs to the new tip.
- `LNDRequireInterceptor` only applies to the primary LND node; additional
nodes started via `StartAdditionalLND*` never set it.
+- `SetupChannelBetween` opens **announced** channels (`Private: false`,
+ set explicitly rather than left to the proto zero value). Tests that
+ route through a third node need the channel in the network graph, and
+ relying on the default would silently flip them to private if lnd ever
+ changed it.
- Container teardown (`Stop`) is guarded by `sync.Once`; a signal handler
also calls `Stop` as a safety net against orphaned containers.
diff --git a/waved/AGENTS.md b/waved/AGENTS.md
index 8caf29d4..4cbaf9c3 100644
--- a/waved/AGENTS.md
+++ b/waved/AGENTS.md
@@ -14,7 +14,9 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
server, and `ActorSystem`. Caches `localMailboxID` (pubkey-derived),
`authSigHex` (Schnorr auth), `clientKeyDesc` (the stable daemon identity
descriptor, behind `clientKeyDescMu`), `mailboxAuthSigs` (per-recipient
- mailbox auth signature memo, behind `mailboxAuthSigsMu`), and a single
+ mailbox auth signature memo, behind `mailboxAuthSigsMu`), the Ark channel
+ process triple (`arkChannelController`, `arkChannelMailboxRuntime`,
+ `arkChannelPeerIngress`, all behind `arkChannelMu`), and a single
`clk` (`clock.Clock`) shared by all sub-stores for deterministic time
injection.
- `RPCServer` — implements the gRPC `DaemonService`. Most write RPCs
@@ -22,7 +24,12 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
validate input locally then `Ask` the relevant actor; `GetRound` and
`ListVTXOs` merge live actor state with persisted SQL rows, while
`GetFeeHistory` and `ListTransactions` are pure SQL reads
- (`rpc_fees.go`).
+ (`rpc_fees.go`). The channel-funding RPCs (`PrepareArkChannelOOR`,
+ `LookupPreparedArkChannelOOR`, `ValidatePreparedArkChannelOOR`,
+ `CommitPreparedArkChannelOOR`, `AbortPreparedArkChannelOOR` in
+ `rpc_ark_channel_oor.go`) and `ExportOORRecoveryPackage`
+ (`rpc_ark_channel_recovery.go`) drive the `arkchannel/oorbridge` and
+ recovery-archive seams instead.
- `Config` — daemon configuration: wallet backend selection, mailbox/chain
backend wiring, `OORConfig`/`OORLimitsConfig` (receive safety caps),
`UnrollConfig` (unilateral-exit fee-bump cadence and cap), and
@@ -33,6 +40,42 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
post-unlock recovery hook.
- `UnrollConfig` / `OORConfig` — subsystem tunables; see `Config.Validate()`
for the invariants each enforces.
+- `ArkChannelController` — the local process boundary published behind
+ `arkchannelrpc.ArkChannelService`. It composes
+ `ArkChannelLifecycleController` (`PromoteVTXO`,
+ `MaterializeAndForceClose`, `RequestCooperativeClose`, `GetChannel`,
+ `PeerMessageHandler`, `Stop`) and `ArkChannelPaymentController`
+ (`SendPayment`, `ReceivePayment`, `PayLightningInvoice`, plus the
+ incoming-payment `Prepare`/`Register`/`Wait` triple the swap subserver
+ calls through `RPCServer.*ArkChannelIncomingPayment`).
+- `NativeArkChannelController` — the concrete implementation, owning one
+ `arkchannel` FSM and one modular lnd endpoint. Built either as a lazy
+ client endpoint (`NewClientArkChannelController`, which loads hub policy
+ on the first lifecycle request) or eagerly as the hub endpoint
+ (`NewHubArkChannelController` over `HubArkChannelControllerConfig`, whose
+ advertised policy comes from `NewHubFundingPeerInfo`).
+ `LightningPaymentResult` is what `PayLightningInvoice` returns.
+- `ArkChannelControllerConfig` — the process-owned dependency set supplied
+ after wallet, database, and authenticated swap-server transport startup:
+ channel store, peer transport, `lwwallet`, chain backend/notifier/fee
+ estimator, the OOR and unroll bridges, identity key, and the funding
+ seams below.
+- `ArkChannelOORPreparer` / `ArkChannelOORLookup` /
+ `ArkChannelReceiveCapitalReserver` — funding callbacks the controller
+ invokes; `Server.prepareArkChannelOOR` and `Server.lookupArkChannelOOR`
+ supply the first two, and only the hub supplies the third.
+- `ArkChannelRecoveryController` — the endpoint-local source archive, spend
+ watcher, and unroll-preparation boundary
+ (`lnruntime.ChannelRecoveryManager` +
+ `arkchannel.ChannelEventSinkBinder` + `unrollbridge.SourcePreparer`),
+ implemented by `arkChannelRecoveryArchive` over
+ `arkChannelSourceWatcher`.
+- `ArkChannelRecoveryRuntime` / `ArkChannelRecoveryRuntimeConfig` — the same
+ unroller, package archive, and channel materializer composed for a host
+ that owns its own database, chain backend, and wallet (swapd's hub
+ endpoint). `ArkChannelUnrollWallet` (built by
+ `NewLNDArkChannelUnrollWallet`) is the shared `txconfirm.Wallet` +
+ `unroll.SweepWallet` surface it needs.
## Relationships
@@ -40,7 +83,9 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
`chainsource`, `lib/actormsg`, `db`, `ledger`, `round`, `txconfirm`,
`unroll`, `vtxo`, `wallet`, `walletcore`, `oor`, `serverconn`, `indexer`,
`arkrpc`, `lndbackend`, `fraud`, `gateway`, `rpc/restclient`,
- `vhtlcrecovery`, `vhtlcrecovery/coordinator`, `vhtlcrecovery/unrollpolicy`.
+ `vhtlcrecovery`, `vhtlcrecovery/coordinator`, `vhtlcrecovery/unrollpolicy`,
+ `arkchannel`, `arkchannel/oorbridge`, `arkchannel/unrollbridge`,
+ `lnruntime`, `rpc/arkchannelrpc`, `chainfees`.
- **Depended on by**: `cmd/waved`.
## Invariants
@@ -117,9 +162,68 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
code must not call `clock.NewDefaultClock()` directly, use `s.clk`.
- Actor startup order in `startWalletDependentActors`: VTXO manager, then
round actor, then the unroll subsystem (`initUnrollSubsystem`), then the
- OOR actor (`initOORActor`). The VTXO manager is constructed with a
+ OOR actor (`initOORActor`), then the Ark channel process
+ (`initArkChannelProcess`). The VTXO manager is constructed with a
`vtxo.LazyChainResolver` placeholder that `initUnrollSubsystem` fills in
later; anything needing that seam must run after `initUnrollSubsystem`.
+ The channel process is last because it needs the durable channel store,
+ the swap-server mailbox edge, and every wallet-owned native dependency.
+- The Ark channel runtime is **opt-in on transport, not on a user flag**:
+ `initArkChannelProcess` returns early unless `SwapConfig.ArkChannelMailbox`
+ is set, which `swapclientserver` publishes as process wiring
+ (`mapstructure:"-"`) and no config file can supply. When it is set the
+ daemon stands up a dedicated `serverconn.Runtime`
+ (`arkchannel-serverconn-<localMailboxID>`) over that authenticated edge,
+ an `lnruntime.PeerMessageIngress` actor for durable BOLT peer messages,
+ and the controller, then publishes all three together under
+ `arkChannelMu` via `setArkChannelProcess`.
+- Channel shutdown order is peer ingress → controller → mailbox runtime,
+ and it runs **before** the main `serverconn` runtime and
+ `actorSystem.Shutdown()`. The mailbox runtime's deferred stop in
+ `runInner` is registered *after* the swap registrar's cleanup defers so
+ it runs first: the channel transport borrows the registrar's edge and
+ must not outlive it.
+- `arkChannelRPCServer` serves `arkchannelrpc.ArkChannelService` on the same
+ authenticated gRPC listener as `DaemonService`. Every method returns
+ `codes.Unavailable` when `getArkChannelController()` is nil, so a caller
+ that dials before the wallet-dependent actors finish can retry instead of
+ reading a startup race as `Internal`. The swap subserver reaches the same
+ controller through `RPCServer.waitArkChannelController`, which polls every
+ `arkChannelControllerPollInterval` (25 ms) until the caller's context
+ ends, because subservers are constructed before the channel process is
+ published.
+- The `channel` macaroon entity (`rpc_auth.go`) gates both surfaces:
+ `channel:read` covers `LookupPreparedArkChannelOOR` and
+ `ArkChannelService.GetChannel`; `channel:write` covers the
+ prepare/validate/commit/abort quartet and every `ArkChannelService`
+ mutation. `ExportOORRecoveryPackage` is deliberately `oor:read` — it
+ exports OOR lineage, not channel state.
+- `NewReceiveScript` accepts `identity_key` to register the daemon's durable
+ `clientKeyDesc` instead of deriving a fresh receive key, for protocol
+ destinations that must survive an independent process restart. It cannot
+ be combined with `idempotency_key` (`InvalidArgument`): the two are
+ opposite retry models, one keyed on a caller string and one on the
+ daemon's own identity. `ensureConfiguredArkChannelCloseDelivery`
+ registers exactly that script for cooperative-close payouts from
+ `startWalletReadyServices` — after the main mailbox ingress is running,
+ since registration is an indexer RPC — bounded by
+ `operatorTermsRefreshTimeout`.
+- The unroll subsystem's `ExitSpendPolicyResolver` is an
+ `unroll.PolicyResolvers` chain: the vHTLC
+ `unrollpolicy.ExitSpendPolicyResolver` first, then `unrollbridge.Resolver`
+ over the Ark channel store. The channel-policy VTXO is recovery-only — it
+ never appears in wallet balance, coin selection, refresh, or ordinary VTXO
+ actor recovery, so its exit policy has to arrive through this resolver.
+- `startPrePONRReaper` runs one controller-lifetime loop over
+ `context.WithoutCancel`, expiring an abandoned pre-PONR channel
+ reservation after `defaultArkChannelPrePONRTimeout` (10 min, scanned every
+ 30 s). Client controllers start it before lnd or the hub is reachable so a
+ locally reserved wallet VTXO is released even if the channel never
+ progresses.
+- `setupLoggers` also installs lnd's own subsystem loggers (`funding`,
+ `CHFD`, `LNWL`, `HSWC`, `INVC`, `routing`, `CNCT`, `BRAR`, `UTXN`, `SWPR`,
+ `CHDB`, `chainio`) into `SubLoggers`, so the embedded native channel stack
+ logs through waved's configuration rather than lnd's package defaults.
- `initUnrollSubsystem` boot ordering is policy-preserving.
`recoverySvc.RestoreNonTerminal` (in-flight vHTLC recovery jobs, each
carrying its durable exit policy) runs **before** the chain resolver is
diff --git a/waved/CLAUDE.md b/waved/CLAUDE.md
index 8caf29d4..4cbaf9c3 100644
--- a/waved/CLAUDE.md
+++ b/waved/CLAUDE.md
@@ -14,7 +14,9 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
server, and `ActorSystem`. Caches `localMailboxID` (pubkey-derived),
`authSigHex` (Schnorr auth), `clientKeyDesc` (the stable daemon identity
descriptor, behind `clientKeyDescMu`), `mailboxAuthSigs` (per-recipient
- mailbox auth signature memo, behind `mailboxAuthSigsMu`), and a single
+ mailbox auth signature memo, behind `mailboxAuthSigsMu`), the Ark channel
+ process triple (`arkChannelController`, `arkChannelMailboxRuntime`,
+ `arkChannelPeerIngress`, all behind `arkChannelMu`), and a single
`clk` (`clock.Clock`) shared by all sub-stores for deterministic time
injection.
- `RPCServer` — implements the gRPC `DaemonService`. Most write RPCs
@@ -22,7 +24,12 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
validate input locally then `Ask` the relevant actor; `GetRound` and
`ListVTXOs` merge live actor state with persisted SQL rows, while
`GetFeeHistory` and `ListTransactions` are pure SQL reads
- (`rpc_fees.go`).
+ (`rpc_fees.go`). The channel-funding RPCs (`PrepareArkChannelOOR`,
+ `LookupPreparedArkChannelOOR`, `ValidatePreparedArkChannelOOR`,
+ `CommitPreparedArkChannelOOR`, `AbortPreparedArkChannelOOR` in
+ `rpc_ark_channel_oor.go`) and `ExportOORRecoveryPackage`
+ (`rpc_ark_channel_recovery.go`) drive the `arkchannel/oorbridge` and
+ recovery-archive seams instead.
- `Config` — daemon configuration: wallet backend selection, mailbox/chain
backend wiring, `OORConfig`/`OORLimitsConfig` (receive safety caps),
`UnrollConfig` (unilateral-exit fee-bump cadence and cap), and
@@ -33,6 +40,42 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
post-unlock recovery hook.
- `UnrollConfig` / `OORConfig` — subsystem tunables; see `Config.Validate()`
for the invariants each enforces.
+- `ArkChannelController` — the local process boundary published behind
+ `arkchannelrpc.ArkChannelService`. It composes
+ `ArkChannelLifecycleController` (`PromoteVTXO`,
+ `MaterializeAndForceClose`, `RequestCooperativeClose`, `GetChannel`,
+ `PeerMessageHandler`, `Stop`) and `ArkChannelPaymentController`
+ (`SendPayment`, `ReceivePayment`, `PayLightningInvoice`, plus the
+ incoming-payment `Prepare`/`Register`/`Wait` triple the swap subserver
+ calls through `RPCServer.*ArkChannelIncomingPayment`).
+- `NativeArkChannelController` — the concrete implementation, owning one
+ `arkchannel` FSM and one modular lnd endpoint. Built either as a lazy
+ client endpoint (`NewClientArkChannelController`, which loads hub policy
+ on the first lifecycle request) or eagerly as the hub endpoint
+ (`NewHubArkChannelController` over `HubArkChannelControllerConfig`, whose
+ advertised policy comes from `NewHubFundingPeerInfo`).
+ `LightningPaymentResult` is what `PayLightningInvoice` returns.
+- `ArkChannelControllerConfig` — the process-owned dependency set supplied
+ after wallet, database, and authenticated swap-server transport startup:
+ channel store, peer transport, `lwwallet`, chain backend/notifier/fee
+ estimator, the OOR and unroll bridges, identity key, and the funding
+ seams below.
+- `ArkChannelOORPreparer` / `ArkChannelOORLookup` /
+ `ArkChannelReceiveCapitalReserver` — funding callbacks the controller
+ invokes; `Server.prepareArkChannelOOR` and `Server.lookupArkChannelOOR`
+ supply the first two, and only the hub supplies the third.
+- `ArkChannelRecoveryController` — the endpoint-local source archive, spend
+ watcher, and unroll-preparation boundary
+ (`lnruntime.ChannelRecoveryManager` +
+ `arkchannel.ChannelEventSinkBinder` + `unrollbridge.SourcePreparer`),
+ implemented by `arkChannelRecoveryArchive` over
+ `arkChannelSourceWatcher`.
+- `ArkChannelRecoveryRuntime` / `ArkChannelRecoveryRuntimeConfig` — the same
+ unroller, package archive, and channel materializer composed for a host
+ that owns its own database, chain backend, and wallet (swapd's hub
+ endpoint). `ArkChannelUnrollWallet` (built by
+ `NewLNDArkChannelUnrollWallet`) is the shared `txconfirm.Wallet` +
+ `unroll.SweepWallet` surface it needs.
## Relationships
@@ -40,7 +83,9 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
`chainsource`, `lib/actormsg`, `db`, `ledger`, `round`, `txconfirm`,
`unroll`, `vtxo`, `wallet`, `walletcore`, `oor`, `serverconn`, `indexer`,
`arkrpc`, `lndbackend`, `fraud`, `gateway`, `rpc/restclient`,
- `vhtlcrecovery`, `vhtlcrecovery/coordinator`, `vhtlcrecovery/unrollpolicy`.
+ `vhtlcrecovery`, `vhtlcrecovery/coordinator`, `vhtlcrecovery/unrollpolicy`,
+ `arkchannel`, `arkchannel/oorbridge`, `arkchannel/unrollbridge`,
+ `lnruntime`, `rpc/arkchannelrpc`, `chainfees`.
- **Depended on by**: `cmd/waved`.
## Invariants
@@ -117,9 +162,68 @@ For field-level detail, use `go doc github.com/lightninglabs/wavelength/waved.<S
code must not call `clock.NewDefaultClock()` directly, use `s.clk`.
- Actor startup order in `startWalletDependentActors`: VTXO manager, then
round actor, then the unroll subsystem (`initUnrollSubsystem`), then the
- OOR actor (`initOORActor`). The VTXO manager is constructed with a
+ OOR actor (`initOORActor`), then the Ark channel process
+ (`initArkChannelProcess`). The VTXO manager is constructed with a
`vtxo.LazyChainResolver` placeholder that `initUnrollSubsystem` fills in
later; anything needing that seam must run after `initUnrollSubsystem`.
+ The channel process is last because it needs the durable channel store,
+ the swap-server mailbox edge, and every wallet-owned native dependency.
+- The Ark channel runtime is **opt-in on transport, not on a user flag**:
+ `initArkChannelProcess` returns early unless `SwapConfig.ArkChannelMailbox`
+ is set, which `swapclientserver` publishes as process wiring
+ (`mapstructure:"-"`) and no config file can supply. When it is set the
+ daemon stands up a dedicated `serverconn.Runtime`
+ (`arkchannel-serverconn-<localMailboxID>`) over that authenticated edge,
+ an `lnruntime.PeerMessageIngress` actor for durable BOLT peer messages,
+ and the controller, then publishes all three together under
+ `arkChannelMu` via `setArkChannelProcess`.
+- Channel shutdown order is peer ingress → controller → mailbox runtime,
+ and it runs **before** the main `serverconn` runtime and
+ `actorSystem.Shutdown()`. The mailbox runtime's deferred stop in
+ `runInner` is registered *after* the swap registrar's cleanup defers so
+ it runs first: the channel transport borrows the registrar's edge and
+ must not outlive it.
+- `arkChannelRPCServer` serves `arkchannelrpc.ArkChannelService` on the same
+ authenticated gRPC listener as `DaemonService`. Every method returns
+ `codes.Unavailable` when `getArkChannelController()` is nil, so a caller
+ that dials before the wallet-dependent actors finish can retry instead of
+ reading a startup race as `Internal`. The swap subserver reaches the same
+ controller through `RPCServer.waitArkChannelController`, which polls every
+ `arkChannelControllerPollInterval` (25 ms) until the caller's context
+ ends, because subservers are constructed before the channel process is
+ published.
+- The `channel` macaroon entity (`rpc_auth.go`) gates both surfaces:
+ `channel:read` covers `LookupPreparedArkChannelOOR` and
+ `ArkChannelService.GetChannel`; `channel:write` covers the
+ prepare/validate/commit/abort quartet and every `ArkChannelService`
+ mutation. `ExportOORRecoveryPackage` is deliberately `oor:read` — it
+ exports OOR lineage, not channel state.
+- `NewReceiveScript` accepts `identity_key` to register the daemon's durable
+ `clientKeyDesc` instead of deriving a fresh receive key, for protocol
+ destinations that must survive an independent process restart. It cannot
+ be combined with `idempotency_key` (`InvalidArgument`): the two are
+ opposite retry models, one keyed on a caller string and one on the
+ daemon's own identity. `ensureConfiguredArkChannelCloseDelivery`
+ registers exactly that script for cooperative-close payouts from
+ `startWalletReadyServices` — after the main mailbox ingress is running,
+ since registration is an indexer RPC — bounded by
+ `operatorTermsRefreshTimeout`.
+- The unroll subsystem's `ExitSpendPolicyResolver` is an
+ `unroll.PolicyResolvers` chain: the vHTLC
+ `unrollpolicy.ExitSpendPolicyResolver` first, then `unrollbridge.Resolver`
+ over the Ark channel store. The channel-policy VTXO is recovery-only — it
+ never appears in wallet balance, coin selection, refresh, or ordinary VTXO
+ actor recovery, so its exit policy has to arrive through this resolver.
+- `startPrePONRReaper` runs one controller-lifetime loop over
+ `context.WithoutCancel`, expiring an abandoned pre-PONR channel
+ reservation after `defaultArkChannelPrePONRTimeout` (10 min, scanned every
+ 30 s). Client controllers start it before lnd or the hub is reachable so a
+ locally reserved wallet VTXO is released even if the channel never
+ progresses.
+- `setupLoggers` also installs lnd's own subsystem loggers (`funding`,
+ `CHFD`, `LNWL`, `HSWC`, `INVC`, `routing`, `CNCT`, `BRAR`, `UTXN`, `SWPR`,
+ `CHDB`, `chainio`) into `SubLoggers`, so the embedded native channel stack
+ logs through waved's configuration rather than lnd's package defaults.
- `initUnrollSubsystem` boot ordering is policy-preserving.
`recoverySvc.RestoreNonTerminal` (in-flight vHTLC recovery jobs, each
carrying its durable exit policy) runs **before** the chain resolver is
diff --git a/waverpc/AGENTS.md b/waverpc/AGENTS.md
index 9d2761a5..014626fd 100644
--- a/waverpc/AGENTS.md
+++ b/waverpc/AGENTS.md
@@ -2,9 +2,10 @@
## Purpose
-Daemon gRPC API definitions for wallet, boarding, round, OOR, unroll, and
-VHTLC-recovery operations, plus the `Sign*` family through which other
-subsystems borrow the daemon identity key (`SignReceiveAuthMessage[Compact]`,
+Daemon gRPC API definitions for wallet, boarding, round, OOR, unroll,
+VHTLC-recovery, and Ark-channel funding operations, plus the `Sign*` family
+through which other subsystems borrow the daemon identity key
+(`SignReceiveAuthMessage[Compact]`,
`SignOORCustomInput`, `SignVTXOForfeit`, `SignOutSwapHtlcAck`,
`SignCreditAccountAuthorization`). Proto source: `waverpc/daemon.proto`.
Generated gRPC, REST-gateway, and mailbox-RPC stubs plus one hand-written
@@ -22,10 +23,16 @@ helper file (`errors.go`) for structured wallet-lifecycle errors.
- `IsWalletNotReadyError(err)` / `WalletNotReadyState(err)` — Match and unpack
the structured error produced above; callers should key off these instead of
matching on message text.
+- `ArkChannelOORPreparationStatus` — enum returned by
+ `LookupPreparedArkChannelOOR`: `UNSPECIFIED`, `ABSENT`, `PENDING`,
+ `PREPARED`, `ACCEPTED`.
## Relationships
-- **Depends on**: `mailbox/rpc` (mailbox-RPC runtime types used by the
+- **Depends on**: `rpc/arkchannelrpc` (`daemon.proto` imports
+ `rpc/arkchannelrpc/ark_channel.proto` for `ChannelTerms`,
+ `ChannelVTXOBinding`, `OORRecoverySource`, and `ChannelRecoveryPackage`),
+ `mailbox/rpc` (mailbox-RPC runtime types used by the
generated mailbox stubs), `google.golang.org/genproto/googleapis/rpc/errdetails`
and `google.golang.org/grpc` (structured errors in `errors.go`),
`grpc-gateway/runtime` (REST gateway in `daemon.pb.gw.go`).
@@ -48,3 +55,21 @@ helper file (`errors.go`) for structured wallet-lifecycle errors.
identity or they will be handed each other's receive scripts. An empty key
keeps the legacy allocate-a-fresh-script behavior; repeating a non-empty key
with a *different* label is rejected rather than silently reallocated.
+- `NewReceiveScriptRequest.identity_key` registers the daemon's durable
+ identity key as the receive destination instead of deriving a fresh one.
+ It is reserved for restart-stable protocol destinations (today, Ark
+ channel cooperative-close payouts) and is **mutually exclusive with
+ `idempotency_key`** — one keys the allocation on a caller string, the
+ other on the daemon's own identity, and combining them is
+ `InvalidArgument`.
+- The Ark channel funding RPCs are a strict five-step protocol:
+ `PrepareArkChannelOOR` reserves daemon liquidity and builds the
+ channel-policy output *without releasing signatures*;
+ `LookupPreparedArkChannelOOR` reconciles the deterministic key without
+ selecting or locking new inputs; `ValidatePreparedArkChannelOOR` re-checks
+ a binding; and exactly one of `CommitPreparedArkChannelOOR` (only after
+ both lnd endpoints persisted the signed backing) or
+ `AbortPreparedArkChannelOOR` (pre-PONR release, `reason` required) ends
+ the reservation. `ExportOORRecoveryPackage` is the separate read-only
+ export of a finalized OOR package and its round ancestry for one exact
+ output.
diff --git a/waverpc/CLAUDE.md b/waverpc/CLAUDE.md
index 9d2761a5..014626fd 100644
--- a/waverpc/CLAUDE.md
+++ b/waverpc/CLAUDE.md
@@ -2,9 +2,10 @@
## Purpose
-Daemon gRPC API definitions for wallet, boarding, round, OOR, unroll, and
-VHTLC-recovery operations, plus the `Sign*` family through which other
-subsystems borrow the daemon identity key (`SignReceiveAuthMessage[Compact]`,
+Daemon gRPC API definitions for wallet, boarding, round, OOR, unroll,
+VHTLC-recovery, and Ark-channel funding operations, plus the `Sign*` family
+through which other subsystems borrow the daemon identity key
+(`SignReceiveAuthMessage[Compact]`,
`SignOORCustomInput`, `SignVTXOForfeit`, `SignOutSwapHtlcAck`,
`SignCreditAccountAuthorization`). Proto source: `waverpc/daemon.proto`.
Generated gRPC, REST-gateway, and mailbox-RPC stubs plus one hand-written
@@ -22,10 +23,16 @@ helper file (`errors.go`) for structured wallet-lifecycle errors.
- `IsWalletNotReadyError(err)` / `WalletNotReadyState(err)` — Match and unpack
the structured error produced above; callers should key off these instead of
matching on message text.
+- `ArkChannelOORPreparationStatus` — enum returned by
+ `LookupPreparedArkChannelOOR`: `UNSPECIFIED`, `ABSENT`, `PENDING`,
+ `PREPARED`, `ACCEPTED`.
## Relationships
-- **Depends on**: `mailbox/rpc` (mailbox-RPC runtime types used by the
+- **Depends on**: `rpc/arkchannelrpc` (`daemon.proto` imports
+ `rpc/arkchannelrpc/ark_channel.proto` for `ChannelTerms`,
+ `ChannelVTXOBinding`, `OORRecoverySource`, and `ChannelRecoveryPackage`),
+ `mailbox/rpc` (mailbox-RPC runtime types used by the
generated mailbox stubs), `google.golang.org/genproto/googleapis/rpc/errdetails`
and `google.golang.org/grpc` (structured errors in `errors.go`),
`grpc-gateway/runtime` (REST gateway in `daemon.pb.gw.go`).
@@ -48,3 +55,21 @@ helper file (`errors.go`) for structured wallet-lifecycle errors.
identity or they will be handed each other's receive scripts. An empty key
keeps the legacy allocate-a-fresh-script behavior; repeating a non-empty key
with a *different* label is rejected rather than silently reallocated.
+- `NewReceiveScriptRequest.identity_key` registers the daemon's durable
+ identity key as the receive destination instead of deriving a fresh one.
+ It is reserved for restart-stable protocol destinations (today, Ark
+ channel cooperative-close payouts) and is **mutually exclusive with
+ `idempotency_key`** — one keys the allocation on a caller string, the
+ other on the daemon's own identity, and combining them is
+ `InvalidArgument`.
+- The Ark channel funding RPCs are a strict five-step protocol:
+ `PrepareArkChannelOOR` reserves daemon liquidity and builds the
+ channel-policy output *without releasing signatures*;
+ `LookupPreparedArkChannelOOR` reconciles the deterministic key without
+ selecting or locking new inputs; `ValidatePreparedArkChannelOOR` re-checks
+ a binding; and exactly one of `CommitPreparedArkChannelOOR` (only after
+ both lnd endpoints persisted the signed backing) or
+ `AbortPreparedArkChannelOOR` (pre-PONR release, `reason` required) ends
+ the reservation. `ExportOORRecoveryPackage` is the separate read-only
+ export of a finalized OOR package and its round ancestry for one exact
+ output.How to apply. Save the diff above and This check is advisory only; it never fails the build. |
070cf3d to
98740b3
Compare
Provide daemon RPCs for promotion, funded receive intents, payments, cooperative settlement, and materialization recovery.
Wire the durable channel store, lnd runtime, OOR coordinator, source watcher, expiry cleanup, and recovery handoff into the daemon.
Add concise commands for channel creation, payments, inspection, cooperative sends, and explicit close operations.
Let system tests expose a public third-party channel so ordinary post-creation routing can be exercised.
Record the operator and client commands needed to exercise the native Ark channel lifecycle locally.
98740b3 to
1232f96
Compare
Summary
This is layer 4 of 5 in the Ark channels stack. It depends on #1191; the final layer is #1139.
Testing
go test ./waved ./waverpc ./cmd/wavecli/waveclicommands ./rpc/restclient ./harnessmake buildmake lint-changed-local