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:
Sulkta 2026-05-10 21:29:40 -07:00
parent 93f0d2ebde
commit 564ba3ccb5
28 changed files with 258 additions and 296 deletions

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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";

View file

@ -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();