chore: scrub internal session-log narrative from code comments
Wide sweep across the codebase to remove leftover artifacts of internal
development sessions, internal entity naming, and audit-code references
that point at non-public docs. The technical reasoning for each piece
of code stays; the "Caught 2026-05-XX while debugging XYZ at preprod"
narrative goes.
Categories scrubbed:
- Dated session-log comments ("Caught/Surfaced/Discovered 2026-05-XX")
→ rewritten as neutral technical reasoning.
- Internal audit codes (AUDIT-H2, AUDIT-C2, AUDIT-M2, AUDIT-H5, etc.)
referencing a non-public audit doc → labels stripped, fix reasoning
kept.
- Internal-entity names in code comments (Sulkta-specific, Sulkta runs
X, Terrapin/TRP as gov-token names) → generic phrasing.
- Test fixture helper `sulkta_cfg` → `test_dao_cfg`; test DAO name
string `"sulkta"` → `"test-dao"`. On-chain addresses in test fixtures
kept (they're real-world wire-byte test data on public chain).
- Cross-references to memory files / non-public audit docs
(`internal notes`, `aiken-escrow/README.md`)
→ reasoning inlined or removed.
- Test names renamed: `decodes_sulkta_live_governor_datum` →
`decodes_live_governor_datum`, `decodes_sulkta_live_proposal_zero` →
`decodes_live_finished_proposal`, etc.
Kept (legitimate):
- Cross-references to in-repo audit docs (aiken-escrow/README.md, aiken-escrow/README.md) — they ARE the
public artifacts being referenced.
- HIGH-1/HIGH-2/MED-2/LOW labels on escrow fixes — these correspond to
findings in the in-repo audit doc.
- TODO markers — legitimate work-still-to-do.
This commit is contained in:
parent
93f0d2ebde
commit
564ba3ccb5
28 changed files with 258 additions and 296 deletions
|
|
@ -38,9 +38,9 @@ use crate::{Network, PaymentKey, ProtocolParams, StakeKey, WalletError};
|
|||
/// Conway DRep registration deposit. Mainnet protocol parameter
|
||||
/// `drep_deposit` is currently 500 ADA. **Use `params.drep_deposit_lovelace`
|
||||
/// instead of this constant** — it's kept here for backward-compat callers
|
||||
/// only. AUDIT-2026-05-06 M-2: hardcoding the deposit means a protocol
|
||||
/// change (or an old DRep registered at a different deposit) will silently
|
||||
/// fail ledger validation. Always pull from current chain params.
|
||||
/// only. Hardcoding the deposit means a protocol change (or an old DRep
|
||||
/// registered at a different deposit) will silently fail ledger validation.
|
||||
/// Always pull from current chain params.
|
||||
pub const DREP_REGISTRATION_DEPOSIT_LOVELACE: u64 = 500_000_000;
|
||||
|
||||
/// Two witnesses (payment + stake) — same overhead as
|
||||
|
|
|
|||
|
|
@ -148,12 +148,11 @@ pub fn build_signed_plutus_spend(
|
|||
|
||||
// AUDIT4-2 fix: pick the SMALLEST ADA-only UTXO that still
|
||||
// qualifies for collateral (≥ 5 ADA), so the LARGEST stays
|
||||
// available for funding the spend. Previously we did the
|
||||
// inverse — collateral got the biggest utxo, funding got
|
||||
// whatever scrap was next, and a typical wallet (one big
|
||||
// change utxo + a tiny self-send leftover) couldn't cover
|
||||
// payout + fee + min_utxo even with billions of lovelace
|
||||
// sitting in the change. Surfaced 2026-05-04 audit-4 phase F2.
|
||||
// available for funding the spend. The inverse approach (give
|
||||
// collateral the biggest utxo) breaks the common case where a
|
||||
// wallet has one large change utxo + a small self-send leftover,
|
||||
// since funding ends up with the scrap and can't cover payout +
|
||||
// fee + min_utxo.
|
||||
//
|
||||
// Collateral is NEVER consumed on the happy path — it's only
|
||||
// seized if the script fails — so its size beyond the 5-ADA
|
||||
|
|
@ -544,10 +543,9 @@ mod tests {
|
|||
|
||||
/// AUDIT4-2 regression. A wallet with one tiny qualifying UTXO
|
||||
/// alongside one huge UTXO must pick the tiny one for collateral
|
||||
/// and the huge one for funding (not the inverse). Pre-fix, the
|
||||
/// huge UTXO became collateral and funding fell back to the
|
||||
/// tiny 5-ADA scrap, too small to cover payout, script-exec
|
||||
/// fee, and change min_utxo. Surfaced 2026-05-04 audit-4 phase F2.
|
||||
/// and the huge one for funding (not the inverse). The inverse
|
||||
/// fails in the common wallet shape where funding then can't
|
||||
/// cover payout + script-exec fee + change min_utxo.
|
||||
#[test]
|
||||
fn picks_smallest_qualifying_collateral_largest_funding() {
|
||||
let payment = payment_from_canonical();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// Conway-era Plutus V3 cost model, 297 params. Snapshot from preprod
|
||||
// epoch 286 (2026-05) but **identical to mainnet epoch 629** —
|
||||
// confirmed 2026-05-04 by parallel Koios `epoch_params` fetch from
|
||||
// `api.koios.rest` and `preprod.koios.rest`. Cost models are
|
||||
// protocol-version parameters, not network parameters; they only
|
||||
// diverge if a network does an experimental hard fork off-cycle.
|
||||
// Conway-era Plutus V3 cost model, 297 params. Snapshot — verified
|
||||
// identical between preprod and mainnet via parallel Koios
|
||||
// `epoch_params` fetches. Cost models are protocol-version
|
||||
// parameters, not network parameters; they only diverge if a network
|
||||
// does an experimental hard fork off-cycle.
|
||||
//
|
||||
// Used by both preprod and mainnet Plutus paths today. Re-snapshot
|
||||
// from mainnet Koios after any major hard fork. If preprod and
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
//! - **Mint**: caller-supplied `(asset_name_hex, quantity)` list under
|
||||
//! the supplied policy (Plutus V1/V2/V3).
|
||||
//! - **Recipient output**: address + lovelace + minted assets +
|
||||
//! any caller-supplied extra assets to forward (e.g. tTRP gov tokens
|
||||
//! any caller-supplied extra assets to forward (e.g. gov tokens
|
||||
//! on a stake bootstrap) + optional inline datum.
|
||||
//! - **Change output**: leftover ADA + leftover input assets (other
|
||||
//! than what was forwarded to the recipient).
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
//! Agora's deployment pattern is the same shape for every "first-time
|
||||
//! mint of a single ST token under a Plutus policy" tx:
|
||||
//! - Governor bootstrap: mint 1 GST → governor_addr + GovernorDatum
|
||||
//! - Stake bootstrap: mint 1 StakeST → stakes_addr + tTRP + StakeDatum
|
||||
//! - Stake bootstrap: mint 1 StakeST → stakes_addr + gov-token + StakeDatum
|
||||
//! - Proposal create: mint 1 ProposalST → proposal_addr + ProposalDatum
|
||||
//!
|
||||
//! All three share the structure; the only differences are the
|
||||
|
|
@ -57,7 +57,7 @@ pub struct PlutusMintAsset {
|
|||
}
|
||||
|
||||
/// Optional non-mint asset to attach to the recipient output.
|
||||
/// Used for e.g. "send tTRP alongside the freshly-minted StakeST"
|
||||
/// Used for e.g. "send gov-tokens alongside the freshly-minted StakeST"
|
||||
/// on a stake bootstrap. Sourced from wallet input UTxOs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtraDestAsset {
|
||||
|
|
@ -90,7 +90,7 @@ pub struct PlutusMintArgs<'a> {
|
|||
pub dest_lovelace: u64,
|
||||
/// Non-mint assets to include on the recipient output. Sourced
|
||||
/// from wallet inputs. Empty for governor bootstrap; non-empty
|
||||
/// for stake bootstrap (tTRP forwarded into the stake).
|
||||
/// for stake bootstrap (gov-tokens forwarded into the stake).
|
||||
pub dest_extra_assets: &'a [ExtraDestAsset],
|
||||
/// Optional inline datum on the recipient output. Required for
|
||||
/// any send to a Plutus script address.
|
||||
|
|
@ -101,9 +101,10 @@ pub struct PlutusMintArgs<'a> {
|
|||
/// for any `pauthorizedBy` / `txSignedBy` check inside a script.
|
||||
/// MCP layer always passes this wallet's payment-key pkh; pass
|
||||
/// extra entries for cosigners. Empty slice = scripts that don't
|
||||
/// check signatories (e.g. Agora's GST policy).
|
||||
/// Caught 2026-05-07 on Agora's stake-policy bootstrap on preprod
|
||||
/// — script erred because owner pkh was absent from signatories.
|
||||
/// check signatories (e.g. Agora's GST policy). Omitting a pkh
|
||||
/// the script checks for will cause the script to error on
|
||||
/// validation even though the corresponding VKey witness is
|
||||
/// present.
|
||||
pub additional_signers: &'a [Hash<28>],
|
||||
}
|
||||
|
||||
|
|
@ -595,11 +596,9 @@ fn prepare_plutus_mint(
|
|||
}
|
||||
|
||||
// Plutus V1/V2/V3 each need their cost-model wired via
|
||||
// language_view so pallas computes script_data_hash on the tx
|
||||
// body. Without it, chain rejects with PPViewHashesDontMatch.
|
||||
// Caught 2026-05-07 attempting Agora's V2 GST-policy bootstrap
|
||||
// mint on preprod — earlier code only set language_view for
|
||||
// V3 and every V2 mint hit the chain rejection.
|
||||
// language_view so pallas computes script_data_hash on the
|
||||
// tx body. Without it, chain rejects with
|
||||
// PPViewHashesDontMatch.
|
||||
match args.policy_version {
|
||||
PlutusVersion::V2 => {
|
||||
staging = staging.language_view(
|
||||
|
|
@ -738,7 +737,7 @@ mod tests {
|
|||
}
|
||||
|
||||
/// Sample preprod governor address (the one Plutarch linker
|
||||
/// produced for our preprod tTRP DAO). Used as the dest.
|
||||
/// produced for our preprod gov-token DAO). Used as the dest.
|
||||
const SAMPLE_GOVERNOR_ADDR: &str =
|
||||
"addr_test1wqlzsnytzs4qv0trmhvw5cuyxnxk0qjq68crn85jj4lhv7qn4wym4";
|
||||
|
||||
|
|
|
|||
|
|
@ -383,19 +383,18 @@ fn output_with_assets(
|
|||
.add_asset(policy, name, *qty)
|
||||
.map_err(|e| WalletError::Derivation(format!("output add_asset: {e}")))?;
|
||||
}
|
||||
// AUDIT4-3 fix: optional inline datum for locking funds at a script
|
||||
// address. Without this, sending to a script address creates an
|
||||
// un-spendable utxo (Babbage/Conway require script-locked outputs
|
||||
// to carry a datum). Caller passes the PlutusData CBOR of whatever
|
||||
// shape the validator expects.
|
||||
// Optional inline datum for locking funds at a script address.
|
||||
// Without this, sending to a script address creates an un-spendable
|
||||
// utxo (Babbage/Conway require script-locked outputs to carry a
|
||||
// datum). Caller passes the PlutusData CBOR of whatever shape the
|
||||
// validator expects.
|
||||
if let Some(datum) = inline_datum_cbor {
|
||||
out = out.set_inline_datum(datum.to_vec());
|
||||
}
|
||||
// 2026-05-07: optional reference-script attached to the output.
|
||||
// This is the on-chain equivalent of `cardano-cli ... --tx-out
|
||||
// --tx-out-reference-script-file ...`. Once deployed, downstream
|
||||
// txs can witness the script via `read_only_input` instead of
|
||||
// inline-witnessing the full CBOR. Required for any DAO/dApp that
|
||||
// Optional reference-script attached to the output — equivalent of
|
||||
// `cardano-cli ... --tx-out-reference-script-file ...`. Once deployed,
|
||||
// downstream txs can witness the script via `read_only_input` instead
|
||||
// of inline-witnessing the full CBOR. Useful for any DAO/dApp that
|
||||
// wants to keep witness sizes manageable when validators are large.
|
||||
if let Some(rs) = reference_script {
|
||||
out = out.set_inline_script(rs.kind, rs.cbor.to_vec());
|
||||
|
|
@ -554,18 +553,13 @@ fn prepare_payment(
|
|||
// Mint paths typically have more lovelace headroom and won't
|
||||
// hit the pass1 floor; if a mint does run tight, the downstream
|
||||
// "insufficient funds for fee" error is informative.
|
||||
// Was 500_000 — surfaced 2026-05-05 zeroing out the mainnet
|
||||
// test wallet (1.8 ADA out of 2 ADA refused upstream).
|
||||
let fee_pass1: u64 = 200_000;
|
||||
// AUDIT5-1: ada-only sends fold sub-min change into fee on the
|
||||
// happy path (see line ~552 below — the `Some(c)` ADA-only arm),
|
||||
// so the selector shouldn't insist on having `min_utxo_lovelace`
|
||||
// worth of room for change. Pass 0 when there are no asset
|
||||
// leftovers; assets-bearing sends still need real change to
|
||||
// route the leftover policy IDs, so keep min_utxo_lovelace there.
|
||||
// Surfaced 2026-05-05 trying to zero out the mainnet test wallet:
|
||||
// 2 ADA balance, 1.8 ADA send refused as "need 3.3M, have 2M"
|
||||
// even though the chain math was fine.
|
||||
// ada-only sends fold sub-min change into fee on the happy path
|
||||
// (the `Some(c)` ADA-only arm below), so the selector shouldn't
|
||||
// insist on having `min_utxo_lovelace` worth of room for change.
|
||||
// Pass 0 when there are no asset leftovers; assets-bearing sends
|
||||
// still need real change to route the leftover policy IDs, so
|
||||
// keep min_utxo_lovelace there.
|
||||
let min_change_required = if target_assets.is_empty() {
|
||||
0
|
||||
} else {
|
||||
|
|
@ -1286,11 +1280,10 @@ mod tests {
|
|||
assert_eq!(result.summary.change_assets[0].policy_id_hex, policy);
|
||||
}
|
||||
|
||||
/// AUDIT4-3 regression: a wallet_send with `to_inline_datum_cbor`
|
||||
/// produces an output carrying that datum. Without this we'd lock
|
||||
/// funds at script addresses with no datum, which Babbage/Conway
|
||||
/// rejects on spend. Surfaced 2026-05-04 audit-4 phase F2 against
|
||||
/// the always-succeeds Aiken validator.
|
||||
/// Regression: a wallet_send with `to_inline_datum_cbor` produces
|
||||
/// an output carrying that datum. Without this we'd lock funds at
|
||||
/// script addresses with no datum, which Babbage/Conway rejects on
|
||||
/// spend.
|
||||
#[test]
|
||||
fn lock_with_inline_datum_attaches_datum_to_output() {
|
||||
use pallas_primitives::Fragment;
|
||||
|
|
@ -1326,13 +1319,10 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// AUDIT5-1 regression: ada-only sends should be allowed to drain
|
||||
/// a wallet down to "all of input - fee" without the selector
|
||||
/// reserving min_utxo for a change output that ends up folded
|
||||
/// into the fee anyway. Pre-fix this returned "need 3300000
|
||||
/// (target+fee+min_change), have 2000000" even though the chain
|
||||
/// math is fine. Caught 2026-05-05 zeroing out the mainnet test
|
||||
/// wallet during Phase 5 real-funds testing.
|
||||
/// Regression: ada-only sends should be allowed to drain a wallet
|
||||
/// down to "all of input - fee" without the selector reserving
|
||||
/// min_utxo for a change output that ends up folded into the fee
|
||||
/// anyway.
|
||||
#[test]
|
||||
fn ada_only_send_can_drain_to_fee() {
|
||||
let payment = payment_from_canonical();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue