plutus spend: fix all 4 chain-level bugs surfaced in preprod audit
PLUTUS-1 (HIGH) — value-not-conserved on happy path. collateral isn't consumed unless script fails, so total_in counted lovelace that wasn't actually available for outputs. now picks a SEPARATE ada-only funding utxo as a regular input alongside the locked utxo; collateral stays collateral. error message tells callers to "split a UTXO first or top up" if a second ada-only utxo isn't available. PLUTUS-2 (HIGH) — collateral containing native assets. chain forbids that; our picker grabbed largest-overall. now filters available_utxos to assets.is_empty() before picking, errors clearly if no ada-only utxo ≥ 5 ADA exists. PLUTUS-3 (HIGH) — fee underestimation. plutus tx fees are size_fee + exunits_fee. only size_fee was being charged. new ProtocolParams::ex_units_fee() does ceil(mem * priceMem) + ceil(steps * priceStep). conway-era prices in defaults (577/10000 mem, 721/10_000_000 steps). fee jumps from ~0.17 ADA → ~1.7 ADA for the default ExUnits budget — matches what chain demanded. PLUTUS-4 (LOW, becomes blocking under the others) — script_data_hash not computed. pallas-txbuilder only computes the body hash field when language_view is set on staging. plutus v3 path now calls .language_view(version, cost_model) when the caller-supplied ProtocolParams::plutus_v3_cost_model is Some. mcp wallet_script_spend populates with the canonical preprod V3 cost model from plutus_cost_models::PLUTUS_V3_COST_MODEL_PREPROD (297 i64 params, fetched from koios epoch_params 2026-05). when ProtocolParams has no cost model, we skip language_view and the chain rejects with PPViewHashesDontMatch — explicit-failure mode, no silent shipping of broken txs. new tests: - ex_units_fee_matches_known_values: 14M mem * 0.0577 + 10B steps * 7.21e-5 ≈ 1.529 ADA ± ceil-rounding. locks the conway price math. - rejects_when_no_funding_input_separate_from_collateral: catches the PLUTUS-1 single-utxo case. - rejects_when_collateral_candidate_has_assets: PLUTUS-2 ada-only. verified on preprod against a real script-locked utxo (the placeholder script we locked 5 tADA at earlier). chain rejection went from 5 distinct errors to 1 (MalformedScriptWitnesses — expected, our placeholder UPLC isn't valid). structural body shape now passes every chain-rule check; only the script bytecode itself fails to compile, which is a test-env limitation (no aiken in our toolchain yet) not a wallet-code limitation. 97 unit tests pass. ProtocolParams gained 5 new fields + ex_units_fee helper; went from Copy to Clone (cost_model is a Vec).
This commit is contained in:
parent
67b86707e8
commit
3bf2be28a7
5 changed files with 297 additions and 32 deletions
|
|
@ -28,6 +28,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use aldabra_chain::{ChainBackend, KoiosClient};
|
||||
use aldabra_core::plutus_cost_models::PLUTUS_V3_COST_MODEL_PREPROD;
|
||||
use aldabra_core::{
|
||||
add_witness, build_signed_cip68_nft_mint, build_signed_mint_with_metadata,
|
||||
build_signed_payment_with_assets, build_signed_plutus_spend, build_signed_stake_delegation,
|
||||
|
|
@ -821,6 +822,13 @@ impl WalletService {
|
|||
lovelace: locked_lovelace,
|
||||
};
|
||||
|
||||
// Plutus paths require the V3 cost model so the chain can
|
||||
// verify script_data_hash. Hardcoded preprod value from
|
||||
// koios epoch_params; future improvement is pulling fresh
|
||||
// from the chain on each call. (PLUTUS-4 audit fix.)
|
||||
let mut params = ProtocolParams::default();
|
||||
params.plutus_v3_cost_model = Some(PLUTUS_V3_COST_MODEL_PREPROD.to_vec());
|
||||
|
||||
let cbor = build_signed_plutus_spend(
|
||||
&self.inner.payment_key,
|
||||
self.inner.network,
|
||||
|
|
@ -834,7 +842,7 @@ impl WalletService {
|
|||
&payout_address,
|
||||
payout_lovelace,
|
||||
budget,
|
||||
&ProtocolParams::default(),
|
||||
¶ms,
|
||||
)
|
||||
.map_err(|e| format!("build/sign plutus spend: {e}"))?;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue