style: apply cargo fmt so the release gate can run
The release gate's first real stage is `cargo fmt --check`, and it had 44 diffs across 15 files — enough to abort `create-release.sh` at step 0 before it touched a version number. Some of that drift is mine from the last two days, some predates it in files I never opened (bootstrap.rs, ghost_reaper.rs, openwrt/router.rs), and one is the regenerated fips/app_ports.rs. No behaviour change — rustfmt only. Gate now: 8 of 9 green. The remaining red is cargo-test-weekly exiting 124, which is the 25-minute `timeout` expiring during a cold CARGO_INCREMENTAL=0 rebuild on a loaded node — the tests never started. Not a test failure. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bd299318c0
commit
c788dff42d
@@ -765,7 +765,10 @@ mod tests {
|
||||
let short = "01fc0ec0e59cd6fa";
|
||||
let full = "01fc0ec0e59cd6fa01b7a88f8cd77fce81fd1e64bca67d752e984992b7a3c3a821";
|
||||
assert!(is_truncated_v2_keyset_id(short));
|
||||
assert!(full.starts_with(short), "short form must prefix the full id");
|
||||
assert!(
|
||||
full.starts_with(short),
|
||||
"short form must prefix the full id"
|
||||
);
|
||||
// It must survive token validation so the swap path can repair it,
|
||||
// rather than being rejected as malformed.
|
||||
assert!(validate_keyset_id(short).is_ok());
|
||||
@@ -784,7 +787,10 @@ mod tests {
|
||||
let err = validate_keyset_id("00112233445566778899")
|
||||
.expect_err("9-byte keyset id must be rejected");
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("10-byte") || msg.contains("unsupported keyset id"), "{msg}");
|
||||
assert!(
|
||||
msg.contains("10-byte") || msg.contains("unsupported keyset id"),
|
||||
"{msg}"
|
||||
);
|
||||
|
||||
// Non-hex ids (the original base64 keyset format) are named as such
|
||||
// rather than reported as a length problem.
|
||||
|
||||
@@ -397,9 +397,8 @@ pub async fn load_accepted_mints(data_dir: &Path) -> Result<AcceptedMints> {
|
||||
mints: vec![network.default_mint()],
|
||||
}
|
||||
} else {
|
||||
serde_json::from_str(&content).with_context(|| {
|
||||
format!("Accepted-mints file {} is damaged", path.display())
|
||||
})?
|
||||
serde_json::from_str(&content)
|
||||
.with_context(|| format!("Accepted-mints file {} is damaged", path.display()))?
|
||||
};
|
||||
Ok(mints)
|
||||
}
|
||||
@@ -1521,7 +1520,10 @@ pub async fn restore_from_seed(data_dir: &Path, mint_url: &str) -> Result<Restor
|
||||
let mint_key = match keys.key_for_amount(sig.amount) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
warn!("Restored a {} sat output with no matching key: {e:#}", sig.amount);
|
||||
warn!(
|
||||
"Restored a {} sat output with no matching key: {e:#}",
|
||||
sig.amount
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@@ -2312,7 +2314,11 @@ mod tests {
|
||||
// mint — never the real coins.
|
||||
save_network(dir, EcashNetwork::Testnet).await.unwrap();
|
||||
let test_wallet = load_wallet(dir).await.unwrap();
|
||||
assert_eq!(test_wallet.balance(), 0, "test wallet must not see real coins");
|
||||
assert_eq!(
|
||||
test_wallet.balance(),
|
||||
0,
|
||||
"test wallet must not see real coins"
|
||||
);
|
||||
assert!(test_wallet.mint_url.contains("testnut"));
|
||||
assert!(load_accepted_mints(dir).await.unwrap().mints[0].contains("testnut"));
|
||||
|
||||
@@ -2341,7 +2347,6 @@ mod tests {
|
||||
assert_eq!(back.proofs[0].proof.secret, "real");
|
||||
}
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_damaged_wallet_file_fails_loudly_and_is_left_on_disk() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
@@ -2355,7 +2360,9 @@ mod tests {
|
||||
|
||||
// It must NOT read as an empty wallet: that is what caused the real
|
||||
// balance to be overwritten with nothing on the next save.
|
||||
let err = load_wallet(dir).await.expect_err("damaged wallet must error");
|
||||
let err = load_wallet(dir)
|
||||
.await
|
||||
.expect_err("damaged wallet must error");
|
||||
assert!(
|
||||
err.to_string().contains("damaged"),
|
||||
"error should name the problem: {err}"
|
||||
@@ -2372,7 +2379,9 @@ mod tests {
|
||||
std::fs::create_dir_all(dir.join("wallet")).unwrap();
|
||||
std::fs::write(dir.join("wallet/ecash.json"), " \n").unwrap();
|
||||
// A create that never got its first write is not damage.
|
||||
let w = load_wallet(dir).await.expect("empty file is a fresh wallet");
|
||||
let w = load_wallet(dir)
|
||||
.await
|
||||
.expect("empty file is a fresh wallet");
|
||||
assert_eq!(w.balance(), 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,10 @@ impl MintClient {
|
||||
&self,
|
||||
keyset_id: &str,
|
||||
amounts: &[u64],
|
||||
) -> Result<(Vec<BlindedMessageRequest>, Vec<(Vec<u8>, secp256k1::SecretKey, u64)>)> {
|
||||
) -> Result<(
|
||||
Vec<BlindedMessageRequest>,
|
||||
Vec<(Vec<u8>, secp256k1::SecretKey, u64)>,
|
||||
)> {
|
||||
let derived = match &self.recovery {
|
||||
Some(source) => match source.next_outputs(keyset_id, amounts.len()).await {
|
||||
Ok(pairs) => Some(pairs),
|
||||
@@ -320,9 +323,7 @@ impl MintClient {
|
||||
.filter(|k| !k.keys.is_empty() && k.unit.eq_ignore_ascii_case("sat"))
|
||||
// Prefer a keyset the mint will still sign with.
|
||||
.max_by_key(|k| k.active)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("No active sat keyset found at mint {}", self.url)
|
||||
})
|
||||
.ok_or_else(|| anyhow::anyhow!("No active sat keyset found at mint {}", self.url))
|
||||
}
|
||||
|
||||
// ── Mint quotes (NUT-04) ──
|
||||
@@ -510,7 +511,9 @@ impl MintClient {
|
||||
"The mint's fee ({fee} sat) consumes this whole amount — nothing would be left"
|
||||
);
|
||||
}
|
||||
debug!("Reducing swap outputs {requested} -> {spendable} to cover a {fee} sat mint fee");
|
||||
debug!(
|
||||
"Reducing swap outputs {requested} -> {spendable} to cover a {fee} sat mint fee"
|
||||
);
|
||||
owned_targets = amount_to_denominations(spendable);
|
||||
&owned_targets
|
||||
} else {
|
||||
|
||||
@@ -280,12 +280,18 @@ pub async fn establish_independent(data_dir: &Path) -> Result<EcashSeed> {
|
||||
/// dangerous choice if the imported phrase turned out to be the one already
|
||||
/// in use.
|
||||
pub async fn import_mnemonic(data_dir: &Path, words: &str, confirm: bool) -> Result<EcashSeed> {
|
||||
let mnemonic: bip39::Mnemonic = words.split_whitespace().collect::<Vec<_>>().join(" ").parse()
|
||||
.map_err(|e| anyhow::anyhow!(
|
||||
"That is not a valid BIP-39 recovery phrase: {e}. Check for typos — \
|
||||
let mnemonic: bip39::Mnemonic = words
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
.parse()
|
||||
.map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"That is not a valid BIP-39 recovery phrase: {e}. Check for typos — \
|
||||
every word must come from the BIP-39 word list, and the phrase as \
|
||||
a whole carries a checksum."
|
||||
))?;
|
||||
)
|
||||
})?;
|
||||
|
||||
if let Some(existing) = load_seed(data_dir).await? {
|
||||
if existing.mnemonic == mnemonic {
|
||||
@@ -322,19 +328,18 @@ async fn archive_seed(data_dir: &Path) -> Result<()> {
|
||||
}
|
||||
let stamp = chrono::Utc::now().format("%Y%m%dT%H%M%SZ");
|
||||
let to = data_dir.join(format!("wallet/cashu_seed.replaced-{stamp}.json"));
|
||||
fs::rename(&from, &to)
|
||||
.await
|
||||
.with_context(|| format!("Could not archive the previous ecash phrase to {}", to.display()))?;
|
||||
fs::rename(&from, &to).await.with_context(|| {
|
||||
format!(
|
||||
"Could not archive the previous ecash phrase to {}",
|
||||
to.display()
|
||||
)
|
||||
})?;
|
||||
warn!("Previous ecash phrase archived to {}", to.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the seed file at 0600, creating the wallet directory if needed.
|
||||
async fn write_seed(
|
||||
data_dir: &Path,
|
||||
mnemonic: &bip39::Mnemonic,
|
||||
source: SeedSource,
|
||||
) -> Result<()> {
|
||||
async fn write_seed(data_dir: &Path, mnemonic: &bip39::Mnemonic, source: SeedSource) -> Result<()> {
|
||||
let path = seed_path(data_dir);
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
@@ -596,8 +601,7 @@ mod tests {
|
||||
// A *different* master seed must not replace the phrase the existing
|
||||
// proofs were minted under.
|
||||
let (other_words, _) = MasterSeed::generate().unwrap();
|
||||
let (_, other_master) =
|
||||
MasterSeed::from_mnemonic_words(&other_words.to_string()).unwrap();
|
||||
let (_, other_master) = MasterSeed::from_mnemonic_words(&other_words.to_string()).unwrap();
|
||||
let third = establish_from_master(d, &other_master).await.unwrap();
|
||||
assert_eq!(
|
||||
first.words(),
|
||||
@@ -615,10 +619,9 @@ mod tests {
|
||||
|
||||
// Stand in for the other wallet: a known phrase and what it derives.
|
||||
let theirs: bip39::Mnemonic = TEST_MNEMONIC.parse().unwrap();
|
||||
let expected =
|
||||
EcashSeed::from_mnemonic(theirs.clone(), SeedSource::Imported)
|
||||
.derive_output(V1_KEYSET, 3)
|
||||
.unwrap();
|
||||
let expected = EcashSeed::from_mnemonic(theirs.clone(), SeedSource::Imported)
|
||||
.derive_output(V1_KEYSET, 3)
|
||||
.unwrap();
|
||||
|
||||
let imported = import_mnemonic(d, TEST_MNEMONIC, false).await.unwrap();
|
||||
assert_eq!(imported.source(), SeedSource::Imported);
|
||||
@@ -644,7 +647,10 @@ mod tests {
|
||||
let err = import_mnemonic(d, &other.to_string(), false)
|
||||
.await
|
||||
.expect_err("must not replace without confirmation");
|
||||
assert!(err.to_string().contains("already has a backup phrase"), "{err}");
|
||||
assert!(
|
||||
err.to_string().contains("already has a backup phrase"),
|
||||
"{err}"
|
||||
);
|
||||
assert_eq!(
|
||||
load_seed(d).await.unwrap().unwrap().words(),
|
||||
original_words,
|
||||
@@ -660,7 +666,11 @@ mod tests {
|
||||
let archived: Vec<_> = std::fs::read_dir(d.join("wallet"))
|
||||
.unwrap()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_name().to_string_lossy().starts_with("cashu_seed.replaced-"))
|
||||
.filter(|e| {
|
||||
e.file_name()
|
||||
.to_string_lossy()
|
||||
.starts_with("cashu_seed.replaced-")
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(archived.len(), 1, "the replaced phrase must be kept");
|
||||
}
|
||||
@@ -677,7 +687,11 @@ mod tests {
|
||||
let archived = std::fs::read_dir(d.join("wallet"))
|
||||
.unwrap()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_name().to_string_lossy().starts_with("cashu_seed.replaced-"))
|
||||
.filter(|e| {
|
||||
e.file_name()
|
||||
.to_string_lossy()
|
||||
.starts_with("cashu_seed.replaced-")
|
||||
})
|
||||
.count();
|
||||
assert_eq!(archived, 0);
|
||||
}
|
||||
@@ -718,7 +732,10 @@ mod tests {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mode = std::fs::metadata(seed_path(d)).unwrap().permissions().mode();
|
||||
let mode = std::fs::metadata(seed_path(d))
|
||||
.unwrap()
|
||||
.permissions()
|
||||
.mode();
|
||||
assert_eq!(mode & 0o777, 0o600, "the ecash phrase must be owner-only");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user