round: separate VTXO activation from input maturity - #1199
Open
bhandras wants to merge 3 commits into
Open
Conversation
bhandras
force-pushed
the
codex/reorg-live-vtxos-20260826
branch
from
August 26, 2026 10:10
3711536 to
e023692
Compare
Add an optional GetInfo field for the confirmation depth at which new round VTXOs become available. Keep the existing minimum-confirmations field focused on boarding-input maturity. New clients fall back to the legacy coupled policy when an older server omits the field. The daemon and SDK expose both values so callers can explain the different safety and availability horizons.
Use the separate VTXO confirmation target when watching a new round commitment. This allows one-confirmation off-chain activity without weakening the operator's input maturity policy. The helper retains the legacy minimum-confirmations fallback. Existing persisted and test terms keep their previous behavior.
A retryable rejection kept the OOR session pending. Operation status lost the classified reason. A chain pause looked like an ordinary in-flight transfer. Carry the existing retry fields on AwaitingSubmitAccepted. Persist them through the existing v5 snapshot records. The session stays retryable. GetOORSession now exposes the typed cause.
bhandras
force-pushed
the
codex/reorg-live-vtxos-20260826
branch
from
August 26, 2026 13:41
e023692 to
7a4ffff
Compare
bhandras
marked this pull request as ready for review
August 26, 2026 15:14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Advertise two independent confirmation policies:
min_confirmations: maturity required for on-chain boarding inputs;vtxo_confirmations: depth at which a newly confirmed round VTXO becomesavailable for off-chain use.
The round client now watches a commitment to the second target. New clients
fall back to the old coupled behavior when an older operator omits the new
field.
This PR also preserves the reason for a retryable operator rejection while an
outgoing OOR session remains pending. That lets callers see why the transfer is
waiting without changing its automatic retry behavior.
Motivation
Input maturity and VTXO activation protect different boundaries.
An on-chain boarding input becomes an economic input to the next commitment.
Requiring it to be mature reduces the chance that a shallow reorganization can
replace that input and change the commitment transaction.
A VTXO created by an already-broadcast commitment is an off-chain output. It
can be useful after one confirmation even while the commitment's economic
inputs remain protected through a deeper wallet and server safety horizon.
Using one field for both concepts forces a product tradeoff that is not
required by the protocol:
This PR makes the two policies explicit. It keeps the client state model small:
the round still has one confirmation wait and one activation transition. Only
the target depth changes.
The retry-reason change serves a separate but related observability need. If an
operator temporarily rejects an OOR input because its root commitment is not
currently spendable, the client already keeps the same session pending and
retries it. Exposing the stored reason makes that wait visible to users and
integration tests.
Architecture
Confirmation policy flow
The policy flows in one direction:
vtxo_confirmationsinarkrpc.GetInfoResponse.wavedconverts the RPC response intotypes.OperatorTerms.GetInfo.VTXOTargetConfirmations()when registering thecommitment confirmation watch.
watch fires.
No new persisted availability state, enum, lineage database, or reorg reducer
is added. The client continues to derive availability from its existing round
state and the operator's advertised confirmation target.
Retryable OOR rejection visibility
An outgoing OOR submit can receive a retryable rejection. The transition stays
in
AwaitingSubmitAccepted, copies the retry delay and reason into that state,and schedules the existing retry.
The outgoing-session snapshot writes those values into the existing v5
retry_afterandfail_reasonfields. Restore reads the same fields back intothe pending state.
GetOORSessioncan therefore report the reason before andafter restart.
This does not add an RPC field or database migration. It does not make the
session terminal. A later retry uses the same session and proceeds through the
existing submit, finalize, and local VTXO update states.
Compatibility
Both confirmation RPC additions are optional protobuf fields.
vtxo_confirmationsfalls back tomin_confirmations, preserving the old behavior.min_confirmationsfor both purposes.OperatorTermswith a zero split field: the helper applies thesame legacy fallback.
The OOR retry metadata reuses fields that already exist in the v5 snapshot.
Older snapshots remain readable. No wire compatibility changes are required.
This makes rollout order flexible. Operators may advertise the policy before
all clients upgrade without changing old-client behavior.
Why this shape
The change keeps policy ownership at the existing boundaries:
Separating two numbers is enough to preserve one-confirmation VTXO activity.
The client does not need to model the operator's wallet leases or duplicate a
server-side canonicality state machine.
User-visible behavior
With an operator policy such as:
boarding funds must be six confirmations deep before they can enter a round,
while VTXOs produced by that round become available after one confirmation.
The daemon and SDK report both values, so applications can explain boarding
latency separately from VTXO activation latency.
If a pending OOR send receives a retryable
INPUT_NOT_SPENDABLErejection, itssession remains pending and now reports the operator reason. The existing retry
loop clears the condition by progressing the same session once the operator
accepts the input again.
Testing
Passed locally:
make rpcgo test ./lib/types ./roundgo test ./waved -run 'Test(OperatorTermsVTXOConfirmations|GetInfoIncludesServerInfo)'go test ./sdk/ark -run '^TestDialRemoteGetInfo$'go test ./oor ./wavedmake lint-changed-localThe OOR tests cover retry reason propagation through the in-memory state and
the v5 outgoing snapshot. The confirmation-policy tests cover explicit split
terms and old-server fallback.
The dependent Lumos daemon E2E uses real bitcoind, LND, Lumos, Bob, and Carol.
It removes the root commitment from the best chain, observes the typed pending
reason through
GetOORSession, re-mines the same commitment txid, and provesthat the same OOR session automatically completes. That test is kept in the
server PR because it owns the reorg trigger and lineage admission policy.
Commits
rpc: advertise VTXO activation depth separatelyround: use advertised VTXO activation depthoor: expose pending submit rejection reason