fix: finalize script tx fee, collateral, and script data hash consistently#2
Open
mgpai22 wants to merge 8 commits into
Open
fix: finalize script tx fee, collateral, and script data hash consistently#2mgpai22 wants to merge 8 commits into
mgpai22 wants to merge 8 commits into
Conversation
…ently Several Complete() finalization steps used stale or zero values that the ledger rejects for script transactions: - estimateFee/estimateExecutionUnits built the sizing/eval body with a zero fee; since the fee field is omitempty a zero value is dropped, undercounting the body size (FeeTooSmallUTxO) and producing a fee-less body that strict evaluators reject. Use a non-zero placeholder fee. - minfee omitted the Conway tiered reference-script fee for script txs. Add TierRefScriptFee plus protocol-param helpers, and a new MinFeeRefScriptCostPerByte field for providers (e.g. BlockFrost) that only expose the flat name. - collateral was sized from the preliminary fee; resize total collateral and the collateral return against the final fee (finalizeCollateral), leaving explicit/manual collateral untouched. - ComputeScriptDataHash encoded an empty datum array (0x80) when there were no datums, shifting the hash and causing ScriptIntegrityHashMismatch for datum-less redeemer txs; append nothing when there are no datums.
TierRefScriptFee accumulated the Conway tiered reference-script fee in float64 and floored at the end. The direction matches the ledger (which floors an exact Rational), and for the integer base price the drift is far below a lovelace, so this was correct in practice. Switch to big.Rat with the multiplier recovered exactly as 6/5 so the result provably equals the ledger minimum for any size and base, matching the reference implementations (CML, mesh) and removing any float-boundary risk on large multi-tier reference scripts. Add oracle tests (single-tier linear, exact match across 1..50 tiers).
…t price Harden the Conway reference-script fee in estimateFee to match the ledger: - count every reference script (native and Plutus), not only Plutus, since the ledger prices all reference scripts by raw byte size - compute the size over the resolved input union unconditionally instead of gating on hasScripts(); totalReferenceScriptSize returns 0 when no reference scripts are present, so non-reference-script txs are unaffected - return a hard error when reference scripts are present but the protocol parameters carry no per-byte price, instead of silently charging zero and building an underpriced transaction
A wallet with a single vkey UTxO previously could not build a script transaction: setCollateral reserved a collateral UTxO out of the coin-selection pool and validateCollateralDistinctFromInputs rejected any overlap between inputs and collateral. This was stricter than the Cardano ledger (and than mesh, lucid, and lucid-evolution), which allow a UTxO to serve as both because collateral is consumed only on phase-2 script failure and regular inputs only on success. setCollateral still prefers a separate eligible vkey UTxO and reserves it, so multi-UTxO wallets keep an identical transaction shape. When that reservation starves coin selection, Complete releases the auto-selected collateral for overlap and retries selection once, so the lone UTxO appears exactly once in the inputs and once in the collateral. validateCollateral keeps duplicate detection and enforces MaxCollateralInputs but no longer rejects input/collateral overlap. finalizeCollateral sizes total_collateral to ceil(fee * collateralPercent / 100) from the final fee and builds the collateral return from the collateral inputs' value alone (excess ADA plus any native assets), never touching the success-path balance. Script-address collateral stays forbidden and caller-pinned collateral is never rewritten.
- validateCollateral now rejects script-address (non-vkey) collateral for caller-pinned AddCollateral, matching the ledger requirement. - finalizeCollateral validates explicit total_collateral (SetCollateralAmount) against ceil(fee*collateralPercent/100) instead of skipping the check. - size collateral inside the fee-convergence loop so the fee accounts for the final collateral total/return footprint. - restore the collateral reservation when the overlap retry fails so a later Complete() starts from consistent state. - prefer the smallest eligible dedicated collateral to reduce unnecessary overlap on multi-UTxO wallets. Adds tests for manual script-address collateral rejection and below-required explicit collateral amount.
- finalizeCollateral now emits total_collateral (body key 18) for an explicit SetCollateralAmount even when combined with AddCollateral, and recomputes the collateral return from the requested amount; previously the requested amount was silently dropped. - reject an explicit collateral amount below the ledger minimum or above the collateral inputs instead of building an invalid tx. - validate fully manual AddCollateral: reject asset-bearing manual collateral that has no return (implicit collateral cannot return assets) and under-funded collateral inputs, while keeping prior ADA-only implicit-collateral behavior. - revert to first-eligible dedicated collateral selection so multi-UTxO wallets keep an identical tx shape. - add a final fee verification after collateral finalization to cover the non-converged fee path. Adds tests for explicit-amount emission, manual asset collateral rejection.
…check - finalizeCollateral now rejects an explicit collateral amount that leaves a sub-min-ADA return instead of silently raising total_collateral above the requested value (which would forfeit the dust on phase-2 failure). Only the auto-sized path absorbs ADA dust. - remove the post-loop fee re-verification: it relied on the same fee=0 size estimate as the main loop so it could not soundly detect underpay, and it added fee-adjacent logic outside the collateral scope. Collateral is sized inside the convergence loop before each estimate, so the converged fee already covers the finalized collateral footprint. Adds a test for explicit-amount dust rejection.
Bring the collateral-input-overlap change onto the script-tx-finalization branch. The overlap collateral code (setCollateral overlap fallback, selectCollateral/collateralEligible helpers, releaseCollateralForOverlap, restoreCollateralReservation, validateCollateral, the 3-mode finalizeCollateral, the isUsed overlap handling, and the Complete two-pass retry) supersedes the older finalizeCollateral and the validateCollateralDistinctFromInputs check. The reference-script fee code (TierRefScriptFee, estimateFee ref-script pricing, totalReferenceScriptSize, placeholder eval fee) and additionalUtxos EvaluateTx support are preserved. A single wallet UTxO can now serve as both a spending input and collateral.
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.
Finalization fixes for script (Plutus) transactions built via
Complete(). Each addresses a ledger rejection observed when submitting reward-withdrawal redeemer txs to preprod (Blockfrost/Ogmios).Fixes
estimateFeeandestimateExecutionUnitsbuilt their sizing/eval body with a zero fee. The bodyfeefield (key 2) isomitempty, so a zero value is dropped from the CBOR: this undercounts the body size (off by the width of the real fee field, causingFeeTooSmallUTxO) and produces a fee-less body that strict evaluators reject (field fee with key 2, not decoded). Both now build with a placeholder fee fromContext.MaxTxFee()(fallback 2 ADA).minfeeomitted the reference-script fee for script txs, causingFeeTooSmallUTxO. Addsbackend.TierRefScriptFee(matching the ledgertierRefScriptFee), protocol-param helpers (RefScriptFeePerByte/RefScriptSizeIncrement/RefScriptMultiplier), and a newMinFeeRefScriptCostPerBytefield for providers (e.g. BlockFrost) that expose only the flat name.totalReferenceScriptSizeresolves reference + spending input script_refs; only applied when the tx has scripts.setCollateral()only had a preliminary (max-by-size) fee;finalizeCollateralrecomputes total collateral and the collateral return from the actual fee (ceil(fee * collateralPercent / 100)), avoidingInsufficientCollateral. Explicit/manual collateral is left untouched.ComputeScriptDataHashencoded an empty datum array (0x80) when there were no datums, shifting the hash by one byte and causingScriptIntegrityHashMismatchfor datum-less redeemer txs. Now appends nothing when there are no datums, matching ConwayUtxoValidateScriptDataHash.Tests
finalization_test.go: script-data-hash omission, tiered ref-script fee, collateral resizing from final fee, and explicit-collateral passthrough.Validated end-to-end on preprod: a reward-withdrawal tx built through
Complete()submits and confirms on-chain.Targets
feat/evaluate-tx-additional-utxos(this branch builds on it).