fix(dao): audit punch list — H-1 to H-4 + M-2 + pallas bump
Lands the high-priority fixes from the 2026-05-06 audit before any mainnet submit of the new vote/cosign/advance/destroy txs. ## H-1: Locked→Finished gate (MCP tool) `dao_proposal_advance_unsigned` now refuses LockedToFinished unless `tx_lower_ms > executing_end`. During the executing period the validator demands gstMoved=true (governor input present); builder doesn't include the governor input, so a tx in that window would fee-burn. The proper Locked→Finished + GAT-mint flow is Phase 4c-bis; this gate keeps us out of the broken middle. ## H-2 + H-4: strict-boundary + tx-upper-inside-period (MCP tool) Validator's pgetRelation is strict on PAfter (`period_end < lb`) and demands `ub <= period_end` on PWithin. Tool now picks PWithin only when `tx_lower_ms >= period_start && tx_upper_ms <= period_end`, PAfter only when `tx_lower_ms > period_end` strictly, and explicit- errors on the boundary-straddling case (when tx validity range crosses out of the target period). Same logic mirrored for the VotingReady→Locked + VotingReady→Finished branches. ## H-3: vote builder lower-bound preflight (MCP tool) `dao_proposal_vote_unsigned` previously checked only validity_upper vs voting_end_ms. Validator demands BOTH `voting_start <= lb` AND `ub <= voting_end`. Vote-too-early would hit "too early or invalid" script error. New preflight on tx_lower_ms vs voting_start. ## M-2: DRep deposit pulled from ProtocolParams Hardcoded constant (500 ADA) was wrong if the protocol changes drep_deposit OR if the DRep was originally registered at a different deposit amount (deregistration must match registration). Added `drep_deposit_lovelace: u64` to ProtocolParams (default 500 ADA), governance.rs build_signed_drep_registration / deregistration now read from params instead of the constant. Constant kept for backward compat with a doc note pointing at the params field. ## Pallas fork bump 507fd9da → 8091abd1 M-4 from the audit landed on the fork: voting_procedures builder debug_assert_ne!s against empty CBOR map (0xa0) and docs the upstream NonEmptyKeyValuePairs::decode footgun. L-1 from the audit was a false finding — the audit subagent misread the constants. PROPOSAL_CREATE_*_EX_UNITS are already at the post-2026-05-05-H-2 values (5M mem / 2G steps per spend, 2M / 1G per mint). The new builders alias these correctly. No change needed.
This commit is contained in:
parent
86a2787b7c
commit
8b48e7224e
4 changed files with 110 additions and 26 deletions
|
|
@ -36,8 +36,11 @@ use crate::tx::InputUtxo;
|
|||
use crate::{Network, PaymentKey, ProtocolParams, StakeKey, WalletError};
|
||||
|
||||
/// Conway DRep registration deposit. Mainnet protocol parameter
|
||||
/// `drep_deposit` is currently 500 ADA. Caller can pass an override
|
||||
/// via `params` if a hardfork changes it; default constant here.
|
||||
/// `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.
|
||||
pub const DREP_REGISTRATION_DEPOSIT_LOVELACE: u64 = 500_000_000;
|
||||
|
||||
/// Two witnesses (payment + stake) — same overhead as
|
||||
|
|
@ -263,7 +266,7 @@ pub fn build_signed_drep_registration(
|
|||
}
|
||||
};
|
||||
|
||||
let cert = Certificate::RegDRepCert(drep_credential, DREP_REGISTRATION_DEPOSIT_LOVELACE, anchor);
|
||||
let cert = Certificate::RegDRepCert(drep_credential, params.drep_deposit_lovelace, anchor);
|
||||
let cert_bytes = minicbor::to_vec(&cert)
|
||||
.map_err(|e| WalletError::Derivation(format!("encode RegDRep cert: {e}")))?;
|
||||
|
||||
|
|
@ -274,7 +277,7 @@ pub fn build_signed_drep_registration(
|
|||
available_utxos,
|
||||
change_address_bech32,
|
||||
vec![cert_bytes],
|
||||
DREP_REGISTRATION_DEPOSIT_LOVELACE,
|
||||
params.drep_deposit_lovelace,
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
|
@ -483,16 +486,15 @@ pub fn build_signed_drep_deregistration(
|
|||
) -> Result<Vec<u8>, WalletError> {
|
||||
let stake_pkh = stake_key.public_key_hash();
|
||||
let drep_credential = StakeCredential::AddrKeyhash(stake_pkh);
|
||||
let cert = Certificate::UnRegDRepCert(drep_credential, DREP_REGISTRATION_DEPOSIT_LOVELACE);
|
||||
let cert = Certificate::UnRegDRepCert(drep_credential, params.drep_deposit_lovelace);
|
||||
let cert_bytes = minicbor::to_vec(&cert)
|
||||
.map_err(|e| WalletError::Derivation(format!("encode UnRegDRep cert: {e}")))?;
|
||||
|
||||
// Negative deposit — we get it back. Two-pass fee accounts for it
|
||||
// by leaving `deposit` at 0 here and letting the wallet output absorb
|
||||
// the refund. Note: pallas-txbuilder writes the deposit as a negative
|
||||
// contribution implicitly via the cert; the change calc here just
|
||||
// needs to know we DON'T owe the protocol anything. Caller should
|
||||
// expect their wallet output to grow by 500 ADA - fee.
|
||||
// Refund equals the deposit originally paid. Critical: this MUST match
|
||||
// what the DRep was originally registered with, not "current chain
|
||||
// drep_deposit." If the protocol changed deposit between registration
|
||||
// and deregistration, caller needs to override `params.drep_deposit_lovelace`
|
||||
// to the original-registration value. Otherwise ledger silently fails.
|
||||
sign_cert_tx_with_refund(
|
||||
payment_key,
|
||||
stake_key,
|
||||
|
|
@ -500,7 +502,7 @@ pub fn build_signed_drep_deregistration(
|
|||
available_utxos,
|
||||
change_address_bech32,
|
||||
vec![cert_bytes],
|
||||
DREP_REGISTRATION_DEPOSIT_LOVELACE,
|
||||
params.drep_deposit_lovelace,
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ pub struct ProtocolParams {
|
|||
/// `None`, Plutus paths skip script_data_hash and the chain will
|
||||
/// reject with `PPViewHashesDontMatch`.
|
||||
pub plutus_v3_cost_model: Option<Vec<i64>>,
|
||||
/// Conway DRep registration deposit (ledger param `drep_deposit`).
|
||||
/// Mainnet default: 500 ADA. Used by `governance::build_signed_drep_*`.
|
||||
/// Pass the chain's current value when constructing — registering
|
||||
/// with the wrong amount fails ledger validation silently.
|
||||
pub drep_deposit_lovelace: u64,
|
||||
}
|
||||
|
||||
impl Default for ProtocolParams {
|
||||
|
|
@ -95,6 +100,7 @@ impl Default for ProtocolParams {
|
|||
// or fetched from `epoch_params`. None by default keeps
|
||||
// the ada-only / mint paths zero-cost.
|
||||
plutus_v3_cost_model: None,
|
||||
drep_deposit_lovelace: 500_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue