feat(resolver): size collateral inputs from the collateral percentage - #354
Draft
scarmuega wants to merge 2 commits into
Draft
feat(resolver): size collateral inputs from the collateral percentage#354scarmuega wants to merge 2 commits into
scarmuega wants to merge 2 commits into
Conversation
A `.tx3` collateral block declares what the author knows — typically `min_amount: fees`. The ledger asks for `collateralPercentage` of the fee (150% on every Cardano network since Alonzo), so a UTxO that satisfies the declared query is a third short of what the ledger accepts, and the tx is rejected for insufficient collateral (seen on Asteria create-ship). Nothing in the resolution path sized collateral: `compile_collateral` only passes through whatever the TIR named. Raise the lovelace floor of every collateral query to `ceil(fees * collateral_percentage / 100)` before the narrow → approximate → assign pipeline runs, so selection picks a UTxO that actually covers the requirement — or fails loudly with `InputNotResolved` rather than emitting a tx the ledger will reject. The percentage is a knob on the new `ResolveOptions`, defaulting to 150. `resolve_tx` keeps its signature and picks up the default; callers holding live protocol parameters can pass their own through `resolve_tx_with_options`. Ref: plans/lang-collateral-sizing-min-ada.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The configurability test asserted which of two candidates the ranker picks at 100%, which the mock store's randomized refs make flaky — green locally, red on CI. Assert the option's actual effect instead: the same pool that fails at 150% resolves at 100%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Plan:
plans/lang-collateral-sizing-min-ada.md(tx3 Trellis root,type: code-change-request,repo: solution/lang/tx3).Draft — item 1 of the plan is implemented and verified in-repo; item 2 needs a chain reproduction this session could not run. See "Done criterion" below.
The defect
A
.tx3collateral block declares what the author knows. Asteria's is representative:The ledger asks for
collateralPercentageof the fee — 150% on every Cardano network since Alonzo — so a UTxO that satisfies the declared query is a third short of what the ledger accepts. Nothing in the resolution path closed the gap:compile_collateral(crates/tx3-cardano/src/compile/mod.rs:450) only passes through the UTxOs the TIR names, and the resolver's input pipeline selected against the declaredmin_amountverbatim. Result: Asteria create-ship CBOR with insufficient collateral.What changed
All of it in
crates/tx3-resolver:CanonicalQuery::raise_lovelace_floor— widens a query's lovelace requirement, never narrows it, and leaves other asset requirements alone.required_collateral(fees, percentage)—ceil(fees * percentage / 100). The ceiling matters: the ledger rejects a collateral balance short by one lovelace.ResolveJob::resolve_inputsapplies the floor to every query withcollateral: true, before narrow → approximate → assign. Selection then picks a UTxO that actually covers the requirement, and when none does, resolution fails withInputNotResolvedinstead of emitting a tx the ledger will reject.ResolveJob::fees), set whereeval_passalready reads it. On the first pass the fee is 0, so sizing is a no-op and the fixpoint loop still gets its first fee estimate.ResolveOptions { max_optimize_rounds, collateral_percentage }, withcollateral_percentagedefaulting toDEFAULT_COLLATERAL_PERCENTAGE = 150.Non-breaking.
resolve_txkeeps its exact signature and picks up the default, sodolos/crates/trpandtx3-hydrabuild unchanged and get the fix on their next bump. Callers holding live protocol parameters can pass their own percentage through the newresolve_tx_with_options. (ResolveJob::execute— the lower-level entry point — now takes&ResolveOptions; no in-org caller uses it.)Why the percentage is a resolver option rather than a
PParamsreadSizing has to happen where UTxO selection happens: the resolver. The resolver is chain-agnostic and reaches the compiler only through
tx3_tir::compile::Compiler, whose surface iscompile+reduce_op— it cannot readtx3_cardano::PParams, andCompilerOplives in the publishedtx3-tircrate (core/tir, a different repository), so aComputeCollateralop is out of scope for a one-repo change. Threading the live value therefore has to come from the caller, which is exactly whatResolveOptionsis for. Adding acollateral_percentagefield totx3_cardano::PParamsin this PR would only have broken the struct literals indolosandtx3-hydrawithout anything reading it yet; it is filed as follow-up work instead.Verification
cargo test --workspace— 350 tests, 0 failures.cargo clippy --all-targets --all-featuresclean,cargo fmt --allapplied.New tests:
inputs::canonical::tests(7) — the percentage arithmetic including the ceiling, and the floor's widen / never-narrow / absent / zero-fee / other-assets-preserved behaviour.inputs::tests::test_collateral_sized_from_percentage_of_fee— a TIR carrying the Asteria collateral shape (min_amount: fees) against a pool holding a UTxO that covers the fee but not 150% of it and one that covers both: the ample one is selected.inputs::tests::test_collateral_percentage_is_configurable— at 100% the tight UTxO is chosen, so the floor really is driven by the option and not hardcoded.inputs::tests::test_collateral_short_of_percentage_does_not_resolve— no candidate reaches 150%:InputNotResolved, not a bad tx.inputs::tests::test_collateral_untouched_on_the_zero_fee_pass— the first pass is unaffected.The
simple_transfergolden fixture is unchanged (it declares no collateral), and no existing hash or fee assertion moved.Done criterion — partially met
The plan's criterion has three parts:
solution/protocols/txpipe/asteriaagainst a devnet/TRP was not run: this session has no chain access, no funded wallet, and no Asteria game state. Someone with a devnet should confirm before this leaves draft.(-32004)change-output min-ada — not addressed. The plan requires a reproduction first ("if it no longer reproduces, record that and change nothing"), and that reproduction is not runnable here. Recorded as a blocker on the plan rather than guessed at. Static finding, for whoever picks it up: there is no min-ada backfill or check anywhere on the output path —compile_output_block(crates/tx3-cardano/src/compile/mod.rs:193) compiles the amount the author wrote, andmin_utxo(...)is an explicit language-level call the author must make. So the gap is real in code; what is undecided is whether a resolver-side backfill is wanted at all, since silently raising an output amount breaks the balance the author wrote.Escalations
Blocker filed on the plan (
--by coder, toorg/founder): who runs the Asteria and Bodega chain reproductions, on what network, before this can leave draft — and whether a change-output min-ada backfill is wanted at all if Bodega still reproduces.Follow-up filed
plans/collateral-percentage-from-live-pparams.md(draft, tx3 root) — carry the livecollateralPercentageprotocol parameter into resolution end to end: add the field totx3_cardano::PParamsand havedolos/crates/trpandtx3-hydrapass it throughresolve_tx_with_options. Crosses three repositories, so it is not this plan's to land.🤖 Generated with Claude Code