diff --git a/.github/nightly-version b/.github/nightly-version index 9f98e758..e67d5713 100644 --- a/.github/nightly-version +++ b/.github/nightly-version @@ -1 +1 @@ -nightly-2024-07-01 +nightly-2024-09-01 diff --git a/networks/monero/primitives/src/lib.rs b/networks/monero/primitives/src/lib.rs index 0b6ed790..47112d1d 100644 --- a/networks/monero/primitives/src/lib.rs +++ b/networks/monero/primitives/src/lib.rs @@ -166,7 +166,14 @@ impl Decoys { /// `offsets` are the positions of each ring member within the Monero blockchain, offset from the /// prior member's position (with the initial ring member offset from 0). pub fn new(offsets: Vec, signer_index: u8, ring: Vec<[EdwardsPoint; 2]>) -> Option { - if (offsets.len() != ring.len()) || (usize::from(signer_index) >= ring.len()) { + if (offsets.len() > usize::from(u8::MAX)) || + (offsets.len() != ring.len()) || + (usize::from(signer_index) >= ring.len()) + { + None?; + } + // Check these offsets form representable positions + if offsets.iter().copied().try_fold(0, u64::checked_add).is_none() { None?; } Some(Decoys { offsets, signer_index, ring }) diff --git a/networks/monero/ringct/bulletproofs/src/batch_verifier.rs b/networks/monero/ringct/bulletproofs/src/batch_verifier.rs index 3898801c..103e6bf7 100644 --- a/networks/monero/ringct/bulletproofs/src/batch_verifier.rs +++ b/networks/monero/ringct/bulletproofs/src/batch_verifier.rs @@ -23,6 +23,11 @@ pub(crate) struct InternalBatchVerifier { impl InternalBatchVerifier { #[must_use] fn verify(self, G: EdwardsPoint, H: EdwardsPoint, generators: &Generators) -> bool { + /* + Technically, this following line can overflow, and joining these `Vec`s _may_ panic if + they're individually acceptable lengths yet their sum isn't. This is so negligible, due to + the amount of memory required, it's dismissed. + */ let capacity = 2 + self.g_bold.len() + self.h_bold.len() + self.other.len(); let mut scalars = Vec::with_capacity(capacity); let mut points = Vec::with_capacity(capacity); diff --git a/networks/monero/ringct/bulletproofs/src/lib.rs b/networks/monero/ringct/bulletproofs/src/lib.rs index 13a52b85..29aa7093 100644 --- a/networks/monero/ringct/bulletproofs/src/lib.rs +++ b/networks/monero/ringct/bulletproofs/src/lib.rs @@ -86,13 +86,16 @@ impl Bulletproof { /// Bulletproofs(+) are logarithmically sized yet linearly timed. Evaluating by their size alone /// accordingly doesn't properly represent the burden of the proof. Monero 'claws back' some of /// the weight lost by using a proof smaller than it is fast to compensate for this. + /// + /// If the amount of outputs specified exceeds the maximum amount of outputs, the result for the + /// maximum amount of outputs will be returned. // https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/ // src/cryptonote_basic/cryptonote_format_utils.cpp#L106-L124 pub fn calculate_bp_clawback(plus: bool, n_outputs: usize) -> (usize, usize) { #[allow(non_snake_case)] let mut LR_len = 0; let mut n_padded_outputs = 1; - while n_padded_outputs < n_outputs { + while n_padded_outputs < n_outputs.min(MAX_COMMITMENTS) { LR_len += 1; n_padded_outputs = 1 << LR_len; } diff --git a/networks/monero/rpc/src/lib.rs b/networks/monero/rpc/src/lib.rs index 72533465..995ccb1a 100644 --- a/networks/monero/rpc/src/lib.rs +++ b/networks/monero/rpc/src/lib.rs @@ -1146,7 +1146,13 @@ impl DecoyRpc for R { )))?; } - let expected_len = if zero_zero_case { 2 } else { (to - start_height) + 1 }; + let expected_len = if zero_zero_case { + 2 + } else { + (to - start_height).checked_add(1).ok_or_else(|| { + RpcError::InternalError("expected length of distribution exceeded usize".to_string()) + })? + }; // Yet this is actually a height if expected_len != distribution.len() { Err(RpcError::InvalidNode(format!( @@ -1161,6 +1167,20 @@ impl DecoyRpc for R { if zero_zero_case { distribution.pop(); } + + // Check the distribution monotonically increases + { + let mut monotonic = 0; + for d in &distribution { + if *d < monotonic { + Err(RpcError::InvalidNode( + "received output distribution didn't increase monotonically".to_string(), + ))?; + } + monotonic = *d; + } + } + Ok(distribution) } } @@ -1271,8 +1291,8 @@ impl DecoyRpc for R { // https://github.com/monero-project/monero/blob // /cc73fe71162d564ffda8e549b79a350bca53c454/src/cryptonote_core // /blockchain.cpp#L3836 - ((out.height + DEFAULT_LOCK_WINDOW) <= height) && - (Timelock::Block(height - 1 + ACCEPTED_TIMELOCK_DELTA) >= + out.height.checked_add(DEFAULT_LOCK_WINDOW).is_some_and(|locked| locked <= height) && + (Timelock::Block(height.wrapping_add(ACCEPTED_TIMELOCK_DELTA - 1)) >= txs[i].prefix().additional_timelock) } else { out.unlocked diff --git a/networks/monero/src/ringct.rs b/networks/monero/src/ringct.rs index 198a73fd..220f289d 100644 --- a/networks/monero/src/ringct.rs +++ b/networks/monero/src/ringct.rs @@ -343,7 +343,13 @@ impl RctPrunable { Ok(match rct_type { RctType::AggregateMlsagBorromean => RctPrunable::AggregateMlsagBorromean { borromean: read_raw_vec(BorromeanRange::read, outputs, r)?, - mlsag: Mlsag::read(ring_length, inputs + 1, r)?, + mlsag: Mlsag::read( + ring_length, + inputs.checked_add(1).ok_or_else(|| { + io::Error::other("reading a MLSAG for more inputs than representable") + })?, + r, + )?, }, RctType::MlsagBorromean => RctPrunable::MlsagBorromean { borromean: read_raw_vec(BorromeanRange::read, outputs, r)?, diff --git a/networks/monero/wallet/Cargo.toml b/networks/monero/wallet/Cargo.toml index af787e49..c0c34606 100644 --- a/networks/monero/wallet/Cargo.toml +++ b/networks/monero/wallet/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/wallet" authors = ["Luke Parker "] edition = "2021" -rust-version = "1.80" +rust-version = "1.82" [package.metadata.docs.rs] all-features = true diff --git a/networks/monero/wallet/address/src/base58check.rs b/networks/monero/wallet/address/src/base58check.rs index 45264bdf..08113bbe 100644 --- a/networks/monero/wallet/address/src/base58check.rs +++ b/networks/monero/wallet/address/src/base58check.rs @@ -94,11 +94,10 @@ pub(crate) fn encode_check(mut data: Vec) -> String { // Decode an arbitrary-length stream of data, with a checksum pub(crate) fn decode_check(data: &str) -> Option> { - if data.len() < CHECKSUM_LEN { + let mut res = decode(data)?; + if res.len() < CHECKSUM_LEN { None?; } - - let mut res = decode(data)?; let checksum_pos = res.len() - CHECKSUM_LEN; if keccak256(&res[.. checksum_pos])[.. CHECKSUM_LEN] != res[checksum_pos ..] { None?; diff --git a/networks/monero/wallet/src/decoys.rs b/networks/monero/wallet/src/decoys.rs index fc776948..96621054 100644 --- a/networks/monero/wallet/src/decoys.rs +++ b/networks/monero/wallet/src/decoys.rs @@ -18,7 +18,7 @@ use crate::{ }; const RECENT_WINDOW: u64 = 15; -const BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME; +const BLOCKS_PER_YEAR: usize = (365 * 24 * 60 * 60) / BLOCK_TIME; #[allow(clippy::cast_precision_loss)] const TIP_APPLICATION: f64 = (DEFAULT_LOCK_WINDOW * BLOCK_TIME) as f64; diff --git a/networks/monero/wallet/src/scan.rs b/networks/monero/wallet/src/scan.rs index 342f000d..79caf3f2 100644 --- a/networks/monero/wallet/src/scan.rs +++ b/networks/monero/wallet/src/scan.rs @@ -232,7 +232,13 @@ impl InternalScanner { res.push(WalletOutput { absolute_id: AbsoluteId { transaction: tx_hash, index_in_transaction: o }, - relative_id: RelativeId { index_on_blockchain: output_index_for_first_ringct_output + o }, + relative_id: RelativeId { + index_on_blockchain: output_index_for_first_ringct_output.checked_add(o).ok_or( + ScanError::InvalidScannableBlock( + "transaction's output's index isn't representable as a u64", + ), + )?, + }, data: OutputData { key: output_key, key_offset, commitment }, metadata: Metadata { additional_timelock: tx.prefix().additional_timelock, diff --git a/networks/monero/wallet/src/send/mod.rs b/networks/monero/wallet/src/send/mod.rs index 8b4e3e34..d0747789 100644 --- a/networks/monero/wallet/src/send/mod.rs +++ b/networks/monero/wallet/src/send/mod.rs @@ -305,12 +305,13 @@ impl SignableTransaction { .payments .iter() .filter_map(|payment| match payment { - InternalPayment::Payment(_, amount) => Some(amount), + InternalPayment::Payment(_, amount) => Some(*amount), InternalPayment::Change(_) => None, }) - .sum::(); + .try_fold(0, u64::checked_add); + let payments_amount = payments_amount.ok_or(SendError::TooManyOutputs)?; let (weight, necessary_fee) = self.weight_and_necessary_fee(); - if in_amount < (payments_amount + necessary_fee) { + if payments_amount.checked_add(necessary_fee).is_none_or(|total_out| in_amount < total_out) { Err(SendError::NotEnoughFunds { inputs: in_amount, outputs: payments_amount, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 73cb338c..8303f09b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.80" +channel = "1.82" targets = ["wasm32-unknown-unknown"] profile = "minimal" components = ["rust-src", "rustfmt", "clippy"] diff --git a/substrate/client/Cargo.toml b/substrate/client/Cargo.toml index 629312c0..2186b26c 100644 --- a/substrate/client/Cargo.toml +++ b/substrate/client/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/substrate/client" authors = ["Luke Parker "] keywords = ["serai"] edition = "2021" -rust-version = "1.74" +rust-version = "1.82" [package.metadata.docs.rs] all-features = true