audit-3 (code cleanup): zero clippy warnings, zero build warnings

prep for deployment. cargo clippy --workspace --all-targets now passes
clean. cargo audit unchanged (same 2 unmaintained-warning macro-support
transitives; no cves).

cleanup applied:
- ProtocolParams construction in tools.rs uses struct-update syntax
  (clippy::field_reassign_with_default).
- main.rs collapsed two else-if branches with identical bodies
  (clippy::if_same_then_else).
- mint/plutus/stake sort_by(|a,b| b.cmp(&a)) → sort_by_key(Reverse(_))
  (clippy::manual_sort_by). 4 sites.
- metadata/mint/tx odd-length hex check uses .is_multiple_of(2)
  (clippy::manual_is_multiple_of). 3 sites.
- stake.rs witness_overhead conditional removed — both branches
  produced TWO_WITNESS_OVERHEAD_BYTES (left over from when
  registration was thought to add a third witness; it doesn't).
  WITNESS_OVERHEAD_BYTES const removed (only the two-witness one
  is used).
- Public spend/mint/stake build_signed_*_with_assets fns get
  #[allow(clippy::too_many_arguments)] — they ARE the API surface.
- ex_units_default_is_generous test gets explicit allow for the
  tautological-on-const assertion (kept the intent comment).

97 unit tests still pass. release build clean.
This commit is contained in:
Cobb 2026-05-04 18:40:35 -07:00
parent 7d59ceffd2
commit f23ff65dad
7 changed files with 30 additions and 25 deletions

View file

@ -171,7 +171,7 @@ pub fn build_cip25_aux_data(
}
fn decode_hex(s: &str) -> Result<Vec<u8>, WalletError> {
if s.len() % 2 != 0 {
if !s.len().is_multiple_of(2) {
return Err(WalletError::Derivation("hex string odd length".into()));
}
let mut out = Vec::with_capacity(s.len() / 2);

View file

@ -181,7 +181,7 @@ fn parse_tx_hash(hex_str: &str) -> Result<Hash<32>, WalletError> {
}
fn parse_asset_name(hex_str: &str) -> Result<Vec<u8>, WalletError> {
if hex_str.len() % 2 != 0 {
if !hex_str.len().is_multiple_of(2) {
return Err(WalletError::Derivation(
"asset_name hex must have even length".into(),
));
@ -259,7 +259,7 @@ fn prepare_mint(
.ok_or_else(|| WalletError::Derivation("amount overflow".into()))?;
let mut sorted: Vec<InputUtxo> = available_utxos.to_vec();
sorted.sort_by(|a, b| b.lovelace.cmp(&a.lovelace));
sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace));
let mut acc: u64 = 0;
let mut selected: Vec<InputUtxo> = Vec::new();
for u in sorted {
@ -703,7 +703,7 @@ pub fn build_signed_cip68_nft_mint(
.ok_or_else(|| WalletError::Derivation("amount overflow".into()))?;
let mut sorted: Vec<InputUtxo> = available_utxos.to_vec();
sorted.sort_by(|a, b| b.lovelace.cmp(&a.lovelace));
sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace));
let mut acc: u64 = 0;
let mut selected: Vec<InputUtxo> = Vec::new();
for u in sorted {

View file

@ -145,7 +145,7 @@ pub fn build_signed_plutus_spend(
.filter(|u| u.assets.is_empty())
.cloned()
.collect();
ada_only.sort_by(|a, b| b.lovelace.cmp(&a.lovelace));
ada_only.sort_by_key(|u| std::cmp::Reverse(u.lovelace));
// Pick collateral — largest ADA-only UTXO ≥ 5 ADA.
let collateral = ada_only
@ -359,7 +359,11 @@ mod tests {
const UNIT_REDEEMER_CBOR: [u8; 3] = [0xd8, 0x79, 0x80];
#[test]
#[allow(clippy::assertions_on_constants)]
fn ex_units_default_is_generous() {
// Tautological today (DEFAULT_EX_UNITS is a const), but the
// intent is to guard against a future shrink that would make
// trivial validators fail to validate. Keep the check.
assert!(DEFAULT_EX_UNITS.mem >= 1_000_000);
assert!(DEFAULT_EX_UNITS.steps >= 100_000_000);
}

View file

@ -24,9 +24,9 @@ use crate::{Network, PaymentKey, ProtocolParams, StakeKey, WalletError};
/// Shelley). Refunded on deregistration.
pub const STAKE_KEY_DEPOSIT_LOVELACE: u64 = 2_000_000;
const WITNESS_OVERHEAD_BYTES: u64 = 128;
/// Two witnesses (payment + stake) instead of just one — used for fee
/// estimation when both keys sign.
/// estimation. Both `register_first` paths sign with both keys, so the
/// witness overhead is constant.
const TWO_WITNESS_OVERHEAD_BYTES: u64 = 256;
/// Decode a `pool1...` bech32 pool ID into a 28-byte Hash.
@ -84,6 +84,7 @@ fn network_id_for(network: Network) -> u8 {
///
/// The tx is signed by both the payment key (body witness) and the
/// stake key (cert witness). Returned CBOR is ready for submission.
#[allow(clippy::too_many_arguments)]
pub fn build_signed_stake_delegation(
payment_key: &PaymentKey,
stake_key: &StakeKey,
@ -129,7 +130,7 @@ pub fn build_signed_stake_delegation(
.ok_or_else(|| WalletError::Derivation("amount overflow".into()))?;
let mut sorted: Vec<InputUtxo> = available_utxos.to_vec();
sorted.sort_by(|a, b| b.lovelace.cmp(&a.lovelace));
sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace));
let mut acc: u64 = 0;
let mut selected: Vec<InputUtxo> = Vec::new();
for u in sorted {
@ -210,15 +211,9 @@ pub fn build_signed_stake_delegation(
.map_err(|e| WalletError::Derivation(format!("conway build (pass1): {e}")))?
.tx_bytes
.0;
// Two witnesses for delegation: payment + stake. Fee estimate
// accounts for both.
let witness_overhead = if register_first {
TWO_WITNESS_OVERHEAD_BYTES
} else {
TWO_WITNESS_OVERHEAD_BYTES
};
let _ = WITNESS_OVERHEAD_BYTES; // silence unused if both branches use the two-witness constant
let est_signed = (unsigned.len() as u64) + witness_overhead;
// Both delegation cases sign with two witnesses (payment + stake);
// registration doesn't add a third.
let est_signed = (unsigned.len() as u64) + TWO_WITNESS_OVERHEAD_BYTES;
let real_fee = params.min_fee_for_size(est_signed);
let token_change = !input_assets.is_empty();

View file

@ -191,7 +191,7 @@ fn parse_policy_id(hex_str: &str) -> Result<pallas_crypto::hash::Hash<28>, Walle
}
fn parse_asset_name(hex_str: &str) -> Result<Vec<u8>, WalletError> {
if hex_str.len() % 2 != 0 {
if !hex_str.len().is_multiple_of(2) {
return Err(WalletError::Derivation(
"asset_name hex must have even length".into(),
));
@ -364,6 +364,7 @@ fn output_with_assets(
Ok(out)
}
#[allow(clippy::too_many_arguments)]
fn build_staging_with_fee(
inputs: &[InputUtxo],
to_addr: &PallasAddress,
@ -632,7 +633,7 @@ fn hex_encode(bytes: &[u8]) -> String {
/// Hex-decode a string into a Vec — used by the cold-sign flow to
/// turn the externally-signed tx hex back into bytes for submission.
pub fn hex_decode(s: &str) -> Result<Vec<u8>, WalletError> {
if s.len() % 2 != 0 {
if !s.len().is_multiple_of(2) {
return Err(WalletError::Derivation("hex string has odd length".into()));
}
let mut out = Vec::with_capacity(s.len() / 2);
@ -671,6 +672,7 @@ pub fn build_signed_payment(
/// Build + sign a Conway-era payment that may include native assets.
/// Returns the signed CBOR bytes ready for `ChainBackend::submit_tx`.
#[allow(clippy::too_many_arguments)]
pub fn build_signed_payment_with_assets(
payment_key: &PaymentKey,
network: Network,
@ -722,6 +724,7 @@ pub fn build_unsigned_payment(
/// signing. Returns the unsigned CBOR + a `PaymentSummary` for human
/// review. Caller signs externally and submits via
/// `ChainBackend::submit_tx`.
#[allow(clippy::too_many_arguments)]
pub fn build_unsigned_payment_with_assets(
network: Network,
available_utxos: &[InputUtxo],
@ -887,7 +890,7 @@ mod tests {
#[test]
fn parse_asset_name_validates_length() {
assert!(parse_asset_name(&"ab".repeat(33)).is_err()); // > 32 bytes
assert!(parse_asset_name(&"deadbeef").is_ok()); // 4 bytes
assert!(parse_asset_name("deadbeef").is_ok()); // 4 bytes
assert!(parse_asset_name("").is_ok()); // 0 bytes — valid (ADA-symbol-like)
}

View file

@ -82,9 +82,10 @@ async fn run() -> Result<()> {
let (payment_key, stake_key, address) = {
let root = if bootstrap_new {
bootstrap::generate_and_save_root_key(&cfg.data_dir)?
} else if mnemonic_path.exists() {
bootstrap::load_or_create_root_key(&cfg.data_dir)?
} else if bootstrap_only {
} else if mnemonic_path.exists() || bootstrap_only {
// Both branches load (or create on the bootstrap path).
// `load_or_create_root_key` itself decides which based on
// whether `mnemonic.age` exists.
bootstrap::load_or_create_root_key(&cfg.data_dir)?
} else {
anyhow::bail!(

View file

@ -826,8 +826,10 @@ impl WalletService {
// 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 params = ProtocolParams {
plutus_v3_cost_model: Some(PLUTUS_V3_COST_MODEL_PREPROD.to_vec()),
..ProtocolParams::default()
};
let cbor = build_signed_plutus_spend(
&self.inner.payment_key,