Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ jobs:
- name: run pedantic clippy on workspace crates
run: |
cargo clippy --all-targets --all-features \
-- --warn clippy::pedantic --deny warnings
-- --warn clippy::pedantic --warn clippy::arithmetic-side-effects --deny warnings
- name: run pedantic clippy on tools/protobuf-compiler
run: |
cargo clippy --manifest-path tools/protobuf-compiler/Cargo.toml \
Expand Down
6 changes: 5 additions & 1 deletion crates/astria-cli/src/commands/rollup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ fn update_yaml_value(

let keys: Vec<&str> = key.split('.').collect();

for &key in keys.iter().take(keys.len() - 1) {
let keys_len_minus_one = keys
.len()
.checked_sub(1)
.expect("`key.split()` should always return at least one value");
for &key in keys.iter().take(keys_len_minus_one) {
target = target
.get_mut(key)
.ok_or_else(|| eyre::eyre!("Invalid key path: {}", key))?;
Expand Down
8 changes: 5 additions & 3 deletions crates/astria-composer/src/executor/bundle_factory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,18 @@ impl SizedBundle {
return Err(SizedBundleError::SequenceActionTooLarge(seq_action));
}

if self.curr_size + seq_action_size > self.max_size {
let new_size = self.curr_size.saturating_add(seq_action_size);

if new_size > self.max_size {
return Err(SizedBundleError::NotEnoughSpace(seq_action));
}

self.rollup_counts
.entry(seq_action.rollup_id)
.and_modify(|count| *count += 1)
.and_modify(|count| *count = count.saturating_add(1))
.or_insert(1);
self.buffer.push(Action::Sequence(seq_action));
self.curr_size += seq_action_size;
self.curr_size = new_size;

Ok(())
}
Expand Down
24 changes: 18 additions & 6 deletions crates/astria-composer/src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ impl Executor {
let mut bundle_factory =
BundleFactory::new(self.max_bytes_per_bundle, self.bundle_queue_capacity);

let reset_time = || Instant::now() + self.block_time;
let reset_time = || {
Instant::now()
.checked_add(self.block_time)
.expect("block_time should not be large enough to cause an overflow")
};

let reason = loop {
select! {
Expand Down Expand Up @@ -288,7 +292,7 @@ impl Executor {
};

let mut bundles_to_drain: VecDeque<SizedBundle> = VecDeque::new();
let mut bundles_drained: u64 = 0;
let mut bundles_drained: Option<u64> = Some(0);

info!("draining already received transactions");

Expand Down Expand Up @@ -360,7 +364,7 @@ impl Executor {
);

nonce = new_nonce;
bundles_drained += 1;
bundles_drained = bundles_drained.and_then(|value| value.checked_add(1));
}
Err(error) => {
error!(
Expand All @@ -386,9 +390,14 @@ impl Executor {
Err(error) => error!(%error, "executor shutdown tasks failed to complete in time"),
}

let number_of_submitted_bundles = if let Some(value) = bundles_drained {
value.to_string()
} else {
format!("more than {}", u64::MAX)
};
if bundles_to_drain.is_empty() {
info!(
number_of_submitted_bundles = bundles_drained,
%number_of_submitted_bundles,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Value for String, so % can be omitted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strings are Debug formatted by default, so we need the % to force Display.

"submitted all outstanding bundles to sequencer during shutdown"
);
} else {
Expand All @@ -397,7 +406,7 @@ impl Executor {
bundles_to_drain.iter().map(SizedBundleReport).collect();

warn!(
number_of_bundles_submitted = bundles_drained,
%number_of_submitted_bundles,
number_of_missing_bundles = report.len(),
missing_bundles = %telemetry::display::json(&report),
"unable to drain all bundles within the allocated time"
Expand Down Expand Up @@ -598,7 +607,10 @@ impl Future for SubmitFut {
metrics::histogram!(crate::metrics_init::TRANSACTIONS_PER_SUBMISSION)
.record(this.bundle.actions_count() as f64);

return Poll::Ready(Ok(*this.nonce + 1));
return Poll::Ready(Ok(this
.nonce
.checked_add(1)
.expect("nonce should not overflow")));
};
match AbciErrorCode::from(code) {
AbciErrorCode::INVALID_NONCE => {
Expand Down
5 changes: 4 additions & 1 deletion crates/astria-conductor/src/block_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ impl<T> BlockCache<T> {
/// Returns the next sequential block if it exists in the cache.
pub(crate) fn pop(&mut self) -> Option<T> {
let block = self.inner.remove(&self.next_height)?;
self.next_height += 1;
self.next_height = self
.next_height
.checked_add(1)
.expect("block height must not exceed `u64::MAX`");
Some(block)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/astria-conductor/src/celestia/block_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ mod test {
};

(
validators::Response::new((height - 1).into(), vec![validator], 1),
validators::Response::new(height.checked_sub(1).unwrap().into(), vec![validator], 1),
address,
commit,
)
Expand Down
33 changes: 21 additions & 12 deletions crates/astria-conductor/src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ fn convert_tendermint_time_to_protobuf_timestamp(value: TendermintTime) -> pbjso
}
}

#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
enum ExecutionKind {
Firm,
Soft,
Expand All @@ -725,15 +725,19 @@ impl std::fmt::Display for ExecutionKind {
}

#[derive(Debug, thiserror::Error)]
#[error(
"contract violated: execution kind: {kind}, current block number {current}, expected \
{expected}, received {actual}"
)]
struct ContractViolation {
kind: ExecutionKind,
current: u32,
expected: u32,
actual: u32,
enum ContractViolation {
#[error(
"contract violated: execution kind: {kind}, current block number {current}, expected \
{expected}, received {actual}"
)]
WrongBlock {
kind: ExecutionKind,
current: u32,
expected: u32,
actual: u32,
},
#[error("contract violated: current height cannot be incremented")]
CurrentBlockNumberIsMax { kind: ExecutionKind, actual: u32 },
}

fn does_block_response_fulfill_contract(
Expand All @@ -745,12 +749,17 @@ fn does_block_response_fulfill_contract(
ExecutionKind::Firm => state.firm_number(),
ExecutionKind::Soft => state.soft_number(),
};
let expected = current + 1;
let actual = block.number();
let expected = current
.checked_add(1)
.ok_or(ContractViolation::CurrentBlockNumberIsMax {
kind,
actual,
})?;
if actual == expected {
Ok(())
} else {
Err(ContractViolation {
Err(ContractViolation::WrongBlock {
kind,
current,
expected,
Expand Down
3 changes: 2 additions & 1 deletion crates/astria-core/src/generated/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/astria-eyre/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn display(
let mut level = 0;
write_layer(level, error, f)?;
while let Some(cause) = error.source() {
level += 1;
level = level.saturating_add(1);
f.write_str(", ")?;
write_layer(level, cause, f)?;
error = cause;
Expand Down
2 changes: 1 addition & 1 deletion crates/astria-grpc-mock-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![allow(unreachable_pub, clippy::pedantic)]
#![allow(unreachable_pub, clippy::pedantic, clippy::arithmetic_side_effects)]

pub mod health {
include!("generated/grpc.health.v1.rs");
Expand Down
9 changes: 6 additions & 3 deletions crates/astria-grpc-mock/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// allow: to be fixed in future PRs. This is used for testing and is not in a critical path.
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(
clippy::module_name_repetitions,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::arithmetic_side_effects
)]

use std::any::Any;

Expand Down
35 changes: 27 additions & 8 deletions crates/astria-merkle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,9 @@ impl<'a> Drop for LeafBuilder<'a> {
// append 2 * 32 = 64 zeros
tree.nodes.extend_from_slice(&[0; 64]);
let size = tree.len();
tree.set_node(size - 1, leaf_hash);
let mut idx = tree.len() - 1;
// safe to unwrap as we already exited above if `tree.nodes.is_empty()`
let mut idx = size.checked_sub(1).expect("tree len must be at least 1");
tree.set_node(idx, leaf_hash);
let root = complete_root(tree.len());
loop {
idx = complete_parent(idx, size);
Expand Down Expand Up @@ -284,7 +285,9 @@ impl Tree {
#[inline]
fn get_node(&self, i: usize) -> [u8; 32] {
assert!(self.is_in_tree(i));
self.nodes[i * 32..(i + 1) * 32].try_into().unwrap()
let low = i.checked_mul(32).unwrap();
let high = i.checked_add(1).unwrap().checked_mul(32).unwrap();
self.nodes[low..high].try_into().unwrap()
}

/// Returns `true` if the index `i` falls inside the Merkle tree.
Expand All @@ -306,7 +309,9 @@ impl Tree {
#[inline]
fn set_node(&mut self, i: usize, val: [u8; 32]) {
assert!(self.is_in_tree(i));
self.nodes[i * 32..(i + 1) * 32].copy_from_slice(&val);
let low = i.checked_mul(32).unwrap();
let high = i.checked_add(1).unwrap().checked_mul(32).unwrap();
self.nodes[low..high].copy_from_slice(&val);
}

/// Constructs the inclusion proof for the i-th leaf of the tree.
Expand Down Expand Up @@ -504,21 +509,32 @@ impl Default for Tree {
///
/// Since leaves are always indexed with even numbers and branches with
/// odd numbers, this is just the formula `i = 2 * j`.
///
/// # Panics
/// Panics if `j` is greater than `usize::MAX / 2`.
#[inline]
fn leaf_index_to_tree_index(j: usize) -> usize {
j * 2
j.checked_mul(2).unwrap()
}

/// Isolates last set bit of an unsigned integer `x` as a mask.
///
/// # Panics
/// Panics if `x` is 0.
#[inline]
fn last_set_bit(x: usize) -> usize {
x - ((x - 1) & x)
// x - ((x - 1) & x)
let x_minus_one = x.checked_sub(1).unwrap();
x.checked_sub(x_minus_one & x).unwrap()
}

/// Isolates the last unset bit of an unsigned integer `x` as a mask.
///
/// # Panics
/// Panics if `x` is `usize::MAX`.
#[inline]
fn last_zero_bit(x: usize) -> usize {
last_set_bit(x + 1)
last_set_bit(x.checked_add(1).unwrap())
}

/// Returns the parent index of a node at index `i` in a perfect binary tree.
Expand Down Expand Up @@ -645,7 +661,10 @@ fn complete_right_child(i: usize, n: usize) -> usize {
if right_child < n {
right_child
} else {
i + 1 + complete_root(n - i - 1)
// i + 1 + complete_root(n - (i + 1))
let i_plus_one = i.checked_add(1).unwrap();
let root = complete_root(n.checked_sub(i_plus_one).unwrap());
i_plus_one.checked_add(root).unwrap()
}
}

Expand Down
10 changes: 9 additions & 1 deletion crates/astria-sequencer-client/src/extension_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,15 @@ pub(super) fn make_path_from_prefix_and_address(
prefix: &'static [u8],
address: [u8; 20],
) -> String {
let mut path = vec![0u8; prefix.len() + address.len() * 2];
let address_hex_len = address
.len()
.checked_mul(2)
.expect("`20 * 2` should not overflow");
let path_len = prefix
.len()
.checked_add(address_hex_len)
.expect("`prefix` should not be greater than `usize::MAX - 40`");
let mut path = vec![0u8; path_len];
path[..prefix.len()].copy_from_slice(prefix);
hex::encode_to_slice(address, &mut path[prefix.len()..]).expect(
"this is a bug: a buffer of sufficient size must have been allocated to hold 20 hex \
Expand Down
4 changes: 2 additions & 2 deletions crates/astria-sequencer-relayer/src/relayer/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,14 @@ impl Stream for BlockStream {

// A new future will be spawned as long as next_height <= latest_height. So
// 10 - 10 = 0, but there 1 future still to be spawned at next height = 10.
lower_limit += Into::<u64>::into(next_height <= latest_height);
lower_limit = lower_limit.saturating_add(u64::from(next_height <= latest_height));

// Add 1 if a fetch is in-flight. Example: if requested and latest heights are
// both 10, then (latest - requested) = 0. But that is only true if the future already
// resolved and its result was returned. If requested height is 9 and latest is 10,
// then `(latest - requested) = 1`, meaning there is one more height to be fetched plus
// the one that's currently in-flight.
lower_limit += Into::<u64>::into(self.future.is_some());
lower_limit = lower_limit.saturating_add(u64::from(self.future.is_some()));

let lower_limit = lower_limit.try_into().expect(
"height differences should convert to usize on all reasonable architectures; 64 bit: \
Expand Down
Loading