fix(dao): audit H-2 + H-5 + H-6 (per internal notes)
H-2: drop ExUnits to 5M/2G for spend, 2M/1G for mint Was 14M/10G each = per-tx Conway cap. With 3 plutus contracts running (governor spend + stake spend + ProposalST mint), total claim 42M/30G exceeds per-tx limit and node rejects pre-phase-2. H-5: propagate malformed wallet asset keys instead of silently dropping Previous filter_map silently dropped any key < 56 chars. Could let a corrupt Koios response burn assets on submit. Now returns explicit Err with the offending UTxO + key. H-6: tighten StakeST detection to asset_name == stake_validator_hash Per Stake/Scripts.hs:188-190 (pscriptHashToTokenName), StakeST asset_name is the stake validator's script hash. Previous code took "first non-gov-token asset" which would silently pick a wrong policy if a stake UTxO accidentally carried a junk NFT. Regression test h6_junk_token_does_not_pollute_stake_st_detection added. 3 of 7 audit punch-list items closed. C-1 + C-2 + C-3 next.
This commit is contained in:
parent
4644fb525f
commit
6955377c61
3 changed files with 128 additions and 43 deletions
|
|
@ -1602,35 +1602,44 @@ impl WalletService {
|
|||
.ok_or_else(|| format!("governor utxo {governor_utxo_ref} no longer present on chain"))?
|
||||
.lovelace;
|
||||
|
||||
let wallet_utxos = self
|
||||
.inner
|
||||
.chain
|
||||
.get_utxos(&self.inner.address)
|
||||
.await
|
||||
.map_err(|e| format!("koios get wallet utxos: {e}"))?
|
||||
.into_iter()
|
||||
.map(|u| DaoWalletUtxo {
|
||||
tx_hash_hex: u.tx_hash,
|
||||
output_index: u.output_index,
|
||||
lovelace: u.lovelace,
|
||||
// Chain backend gives `assets: BTreeMap<concatenated_key, qty>`
|
||||
// where the key is `policy_id_hex || asset_name_hex` with the
|
||||
// policy taking the first 56 chars (28 bytes). Split for
|
||||
// pallas-txbuilder which wants the parts separate.
|
||||
assets: u
|
||||
.assets
|
||||
.into_iter()
|
||||
.filter_map(|(k, q)| {
|
||||
if k.len() >= 56 {
|
||||
let (p, n) = k.split_at(56);
|
||||
Some((p.to_string(), n.to_string(), q))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect();
|
||||
let wallet_utxos: Vec<DaoWalletUtxo> = {
|
||||
let raw = self
|
||||
.inner
|
||||
.chain
|
||||
.get_utxos(&self.inner.address)
|
||||
.await
|
||||
.map_err(|e| format!("koios get wallet utxos: {e}"))?;
|
||||
|
||||
// AUDIT-H5 fix: assets in the chain backend are
|
||||
// `BTreeMap<policy_id_hex || asset_name_hex, qty>`. Previous
|
||||
// implementation silently dropped any key < 56 chars via filter_map
|
||||
// — that could let a corrupt Koios response burn assets on submit.
|
||||
// Now: any malformed key surfaces as an explicit error.
|
||||
let mut out = Vec::with_capacity(raw.len());
|
||||
for u in raw {
|
||||
let mut assets = Vec::with_capacity(u.assets.len());
|
||||
for (k, q) in u.assets {
|
||||
if k.len() < 56 {
|
||||
return Err(format!(
|
||||
"malformed asset key in wallet utxo {tx_hash}#{idx}: \
|
||||
{k:?} is {len} chars, need ≥ 56 (policy_id_hex || asset_name_hex)",
|
||||
tx_hash = u.tx_hash,
|
||||
idx = u.output_index,
|
||||
len = k.len(),
|
||||
));
|
||||
}
|
||||
let (p, n) = k.split_at(56);
|
||||
assets.push((p.to_string(), n.to_string(), q));
|
||||
}
|
||||
out.push(DaoWalletUtxo {
|
||||
tx_hash_hex: u.tx_hash,
|
||||
output_index: u.output_index,
|
||||
lovelace: u.lovelace,
|
||||
assets,
|
||||
});
|
||||
}
|
||||
out
|
||||
};
|
||||
|
||||
// ScriptRefs must be populated before this tool can build a tx.
|
||||
// For Sulkta the values are known from the audit; user must pass
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue