Response to usage of unwrap in non-test code

This commit replaces all usage of `unwrap` with `expect` within
`networks/monero`, clarifying why the panic risked is unreachable. This commit
also replaces some uses of `unwrap` with solutions which are guaranteed not to
fail.

Notably, compilation on 128-bit systems is prevented, ensuring
`u64::try_from(usize::MAX)` will never panic at runtime.

Slight breaking changes are additionally included as necessary to massage out
some avoidable panics.
This commit is contained in:
Luke Parker
2025-08-08 21:28:47 -04:00
parent 4f65a0b147
commit a5f4c450c6
31 changed files with 310 additions and 169 deletions

View File

@@ -51,7 +51,7 @@ impl BlockHeader {
/// Serialize the BlockHeader to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
self.write(&mut serialized).expect("write failed but <Vec as io::Write> doesn't fail");
serialized
}
@@ -111,7 +111,7 @@ impl Block {
/// Serialize the Block to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
self.write(&mut serialized).expect("write failed but <Vec as io::Write> doesn't fail");
serialized
}
@@ -122,7 +122,13 @@ impl Block {
pub fn serialize_pow_hash(&self) -> Vec<u8> {
let mut blob = self.header.serialize();
blob.extend_from_slice(&merkle_root(self.miner_transaction.hash(), &self.transactions));
write_varint(&(1 + u64::try_from(self.transactions.len()).unwrap()), &mut blob).unwrap();
write_varint(
&(1 +
u64::try_from(self.transactions.len())
.expect("amount of transactions in block exceeded u64::MAX")),
&mut blob,
)
.expect("write failed but <Vec as io::Write> doesn't fail");
blob
}
@@ -132,7 +138,11 @@ impl Block {
// Monero pre-appends a VarInt of the block-to-hash'ss length before getting the block hash,
// but doesn't do this when getting the proof of work hash :)
let mut hashing_blob = Vec::with_capacity(9 + hashable.len());
write_varint(&u64::try_from(hashable.len()).unwrap(), &mut hashing_blob).unwrap();
write_varint(
&u64::try_from(hashable.len()).expect("length of block hash's preimage exceeded u64::MAX"),
&mut hashing_blob,
)
.expect("write failed but <Vec as io::Write> doesn't fail");
hashing_blob.append(&mut hashable);
let hash = keccak256(hashing_blob);

View File

@@ -28,7 +28,7 @@ pub(crate) fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
let mut paired_hashes = Vec::with_capacity(overage);
while let Some(left) = rightmost.next() {
let right = rightmost.next().unwrap();
let right = rightmost.next().expect("rightmost is of even length");
paired_hashes.push(keccak256([left.as_ref(), &right].concat()));
}
drop(rightmost);

View File

@@ -326,7 +326,9 @@ impl RctPrunable {
/// Serialize the RctPrunable to a `Vec<u8>`.
pub fn serialize(&self, rct_type: RctType) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized, rct_type).unwrap();
self
.write(&mut serialized, rct_type)
.expect("write failed but <Vec as io::Write> doesn't fail");
serialized
}
@@ -441,7 +443,7 @@ impl RctProofs {
/// Serialize the RctProofs to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
self.write(&mut serialized).expect("write failed but <Vec as io::Write> doesn't fail");
serialized
}

View File

@@ -53,7 +53,7 @@ impl Input {
/// Serialize the Input to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
res
}
@@ -106,7 +106,7 @@ impl Output {
/// Write the Output to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(8 + 1 + 32);
self.write(&mut res).unwrap();
self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
res
}
@@ -163,7 +163,7 @@ impl Timelock {
/// Serialize the Timelock to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(1);
self.write(&mut res).unwrap();
self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
res
}
@@ -259,8 +259,8 @@ impl TransactionPrefix {
fn hash(&self, version: u64) -> [u8; 32] {
let mut buf = vec![];
write_varint(&version, &mut buf).unwrap();
self.write(&mut buf).unwrap();
write_varint(&version, &mut buf).expect("write failed but <Vec as io::Write> doesn't fail");
self.write(&mut buf).expect("write failed but <Vec as io::Write> doesn't fail");
keccak256(buf)
}
}
@@ -451,7 +451,7 @@ impl<P: PotentiallyPruned> Transaction<P> {
/// Write the Transaction to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(2048);
self.write(&mut res).unwrap();
self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
res
}
@@ -493,15 +493,16 @@ impl<P: PotentiallyPruned> Transaction<P> {
let mut buf = Vec::with_capacity(512);
// We don't use `self.write` as that may write the signatures (if this isn't pruned)
write_varint(&self.version(), &mut buf).unwrap();
prefix.write(&mut buf).unwrap();
write_varint(&self.version(), &mut buf)
.expect("write failed but <Vec as io::Write> doesn't fail");
prefix.write(&mut buf).expect("write failed but <Vec as io::Write> doesn't fail");
// We explicitly write the signatures ourselves here
let PrunableHash::V1(signatures) = prunable else {
panic!("hashing v1 TX with non-v1 prunable data")
};
for signature in signatures {
signature.write(&mut buf).unwrap();
signature.write(&mut buf).expect("write failed but <Vec as io::Write> doesn't fail");
}
keccak256(buf)
@@ -513,7 +514,10 @@ impl<P: PotentiallyPruned> Transaction<P> {
if let Some(proofs) = proofs {
let mut buf = Vec::with_capacity(512);
proofs.base().write(&mut buf, proofs.rct_type()).unwrap();
proofs
.base()
.write(&mut buf, proofs.rct_type())
.expect("write failed but <Vec as io::Write> doesn't fail");
hashes.extend(keccak256(&buf));
} else {
// Serialization of RctBase::Null
@@ -540,7 +544,10 @@ impl Transaction<NotPruned> {
Transaction::V2 { proofs, .. } => {
self.hash_with_prunable_hash(PrunableHash::V2(if let Some(proofs) = proofs {
let mut buf = Vec::with_capacity(1024);
proofs.prunable.write(&mut buf, proofs.rct_type()).unwrap();
proofs
.prunable
.write(&mut buf, proofs.rct_type())
.expect("write failed but <Vec as io::Write> doesn't fail");
keccak256(buf)
} else {
[0; 32]
@@ -563,7 +570,10 @@ impl Transaction<NotPruned> {
Transaction::V2 { proofs, .. } => self.hash_with_prunable_hash({
let Some(proofs) = proofs else { None? };
let mut buf = Vec::with_capacity(1024);
proofs.prunable.signature_write(&mut buf).unwrap();
proofs
.prunable
.signature_write(&mut buf)
.expect("write failed but <Vec as io::Write> doesn't fail");
PrunableHash::V2(keccak256(buf))
}),
})