crate::ix!();
#[inline]
pub fn checksum4_sha256d(payload: &[u8]) -> [u8; 4] {
trace!(
payload_len = payload.len(),
"Base58Check checksum: computing SHA256d and taking first 4 bytes"
);
let mut hasher = Hash256::default();
hasher.write(payload);
let mut digest = [0u8; Hash256::OUTPUT_SIZE];
hasher.finalize(&mut digest);
let out = [digest[0], digest[1], digest[2], digest[3]];
debug!(checksum = %hex::encode(out), "Base58Check checksum computed");
out
}
pub unsafe fn decode_base_58check_raw(
mut psz: *const u8,
vch_ret: &mut Vec<u8>,
max_ret_len: i32
) -> bool {
let budget = if max_ret_len > i32::MAX - 4 { i32::MAX } else { max_ret_len + 4 };
debug!(max_ret_len, budget, "decode_base_58check_raw: starting");
if !decode_base58_raw(psz, vch_ret, budget) {
warn!("decode_base_58check_raw: underlying Base‑58 decode failed");
vch_ret.clear();
return false;
}
if vch_ret.len() < 4 {
warn!(decoded_len = vch_ret.len(), "decode_base_58check_raw: insufficient length for checksum");
vch_ret.clear();
return false;
}
let payload_len = vch_ret.len() - 4;
trace!(payload_len, total = vch_ret.len(), "decode_base_58check_raw: verifying checksum");
let expected = checksum4_sha256d(&vch_ret[..payload_len]);
if vch_ret[payload_len..] != expected {
error!(
found = %hex::encode(&vch_ret[payload_len..]),
expected = %hex::encode(expected),
"decode_base_58check_raw: checksum mismatch"
);
vch_ret.clear();
return false;
}
vch_ret.truncate(payload_len);
info!(payload_len, "decode_base_58check_raw: success");
true
}
pub fn decode_base_58check(input: &str, vch_ret: &mut Vec<u8>, max_ret_len: usize) -> bool {
debug!(input_len = input.len(), max_ret_len, "decode_base_58check: starting");
if max_ret_len > usize::MAX - 4 {
warn!("decode_base_58check: max_ret_len overflow guard triggered");
return false;
}
if !decode_base58(input, vch_ret, max_ret_len + 4) {
warn!("decode_base_58check: underlying Base‑58 decode failed");
vch_ret.clear();
return false;
}
if vch_ret.len() < 4 {
warn!(decoded_len = vch_ret.len(), "decode_base_58check: insufficient length for checksum");
vch_ret.clear();
return false;
}
let payload_len = vch_ret.len() - 4;
trace!(payload_len, total = vch_ret.len(), "decode_base_58check: verifying checksum");
let expected = checksum4_sha256d(&vch_ret[..payload_len]);
if vch_ret[payload_len..] != expected {
error!(
found = %hex::encode(&vch_ret[payload_len..]),
expected = %hex::encode(expected),
"decode_base_58check: checksum mismatch"
);
vch_ret.clear();
return false;
}
vch_ret.truncate(payload_len);
info!(payload_len, "decode_base_58check: success");
true
}
#[cfg(test)]
mod decode_check_spec {
use super::*;
#[traced_test]
fn base58check_roundtrip_randomised() {
let mut state = 0x1234_5678_9ABC_DEF0u64;
for case in 0..128 {
state = state.wrapping_mul(6364136223846793005u64).wrapping_add(1);
let len = (state & 0x1F) as usize + 1; let mut data = (0..len).map(|i| (state >> ((i * 7) % 56)) as u8).collect::<Vec<_>>();
let encoded = encode_base_58check(&data);
trace!(case, len, ?encoded, "encoded random payload");
let mut decoded = Vec::new();
assert!(
decode_base_58check(&encoded, &mut decoded, ((len + 4) as i32).try_into().unwrap()),
"case {case}: decode failed"
);
assert_eq!(decoded, data, "case {case}: round‑trip mismatch");
}
}
#[traced_test]
fn detects_bad_checksum() {
let payload = b"bitcoin";
let mut encoded = encode_base_58check(payload);
encoded.pop();
encoded.push('2'); let mut sink = Vec::new();
assert!(
!decode_base_58check(&encoded, &mut sink, 128),
"corrupted checksum was not detected"
);
}
}