-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Catch usize conversion and Err more consistently #1499
base: testnet3
Are you sure you want to change the base?
Conversation
let mut z_b_s = BTreeMap::new(); | ||
for (circuit_id, circuit_state) in state.circuit_specific_states.iter() { | ||
let mut z_b_i = Vec::with_capacity(usize::try_from(circuit_state.batch_size)?); | ||
for i in 0..circuit_state.batch_size { | ||
let z_b = witness_label(*circuit_id, "z_b", usize::try_from(i)?); | ||
z_b_i.push(LinearCombination::new(z_b.clone(), [(F::one(), z_b)])); | ||
} | ||
z_b_s.insert(circuit_id, z_b_i); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use Itertools::try_collect
here, instead of rewriting as a for loop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to return the Result, I have to unwrap the result in order to use z_b on line 246. I got it to compile if we return a Result
anyway for all paths, but I don't think we'd be happy with this (the compiler asks for those type annotations:
let z_b_s = state
.circuit_specific_states
.iter()
.map(|(&circuit_id, circuit_state)| {
let z_b_i = (0..circuit_state.batch_size)
.map(|i| {
let z_b = witness_label(circuit_id, "z_b", usize::try_from(i)?);
Ok::<LinearCombination<F>, std::num::TryFromIntError>(LinearCombination::new(z_b.clone(), [(F::one(), z_b)]))
}).collect::<Result<Vec<_>,_>>();
Ok::<(CircuitId, Vec<LinearCombination<F>>), std::num::TryFromIntError>((circuit_id, z_b_i?))
})
.collect::<Result<BTreeMap<_, _>,_>>()?;
Meanwhile, online fora on related issues keep suggesting to just use a forloop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it down to 4916a3c but it's still more code than the for-loop
}) | ||
.collect::<BTreeMap<_, _>>(); | ||
.collect::<Result<Vec<_>, _>>()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto here, I think we can use try_collect
to still collect into a BTreeMap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we do that, the code that uses z_b_s_at_beta
should go back to its old state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't use try_collect
because I need to annotate the types, but I can use ?
within the map now and collect: 4916a3c
@@ -74,13 +74,14 @@ impl<F: PrimeField, MM: MarlinMode> AHPForR1CS<F, MM> { | |||
#[allow(clippy::type_complexity)] | |||
pub fn prover_first_round<'a, R: RngCore>( | |||
mut state: prover::State<'a, F, MM>, | |||
batch_sizes: impl Iterator<Item = (&'a CircuitId, &'a usize)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we passing this separately? Isn't it included in state
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is in state, but we created this iterator already earlier so I thought why not reuse it for line 138 below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we passing this in if it's in state?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comment right above yours... But since you both think it's weird, removed it: 7f2a6c8
@@ -94,7 +95,7 @@ impl<F: PrimeField, MM: MarlinMode> AHPForR1CS<F, MM> { | |||
let c_domain = circuit_state.constraint_domain; | |||
let i_domain = circuit_state.input_domain; | |||
|
|||
let mut circuit_r_b_s = Vec::with_capacity(batch_size); | |||
let mut circuit_r_b_s = Vec::with_capacity(z_a.len()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this change? We do still have batch_size
in scope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rng: &mut R, | ||
) -> Result<prover::State<'a, F, MM>, AHPError> { | ||
let round_time = start_timer!(|| "AHP::Prover::FirstRound"); | ||
let mut r_b_s = Vec::with_capacity(state.circuit_specific_states.len()); | ||
let mut job_pool = snarkvm_utilities::ExecutionPool::with_capacity(3 * state.total_instances); | ||
let mut job_pool = snarkvm_utilities::ExecutionPool::with_capacity(usize::try_from(3 * state.total_instances)?); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the following works, I think we should switch to it, instead of using the more verbose usize::try_from
let mut job_pool = snarkvm_utilities::ExecutionPool::with_capacity(usize::try_from(3 * state.total_instances)?); | |
let mut job_pool = snarkvm_utilities::ExecutionPool::with_capacity((3 * state.total_instances).try_into()?); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -114,13 +114,13 @@ impl<'a, F: PrimeField, MM: MarlinMode> State<'a, F, MM> { | |||
|
|||
let first_padded_public_inputs = &variable_assignments[0].0; | |||
let input_domain = EvaluationDomain::new(first_padded_public_inputs.len()).unwrap(); | |||
let batch_size = variable_assignments.len(); | |||
let batch_size = u32::try_from(variable_assignments.len())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, let's change this to try_into()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let mut x_polys = Vec::with_capacity(batch_size); | ||
let mut padded_public_variables = Vec::with_capacity(batch_size); | ||
let mut private_variables = Vec::with_capacity(batch_size); | ||
let mut z_as = Vec::with_capacity(variable_assignments.len()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should make a batch_size_usize
local variable and use that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed would be easier to read 5ebe7b2
let mut batch_sizes = BTreeMap::new(); | ||
for (circuit_id, batch_size) in | ||
state.circuit_specific_states.iter().map(|(circuit_id, s)| (circuit_id, s.batch_size)) | ||
{ | ||
batch_sizes.insert(*circuit_id, usize::try_from(batch_size)?); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, try_collect
and try_into
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
use std::{collections::BTreeMap, fmt}; | ||
|
||
/// Stores a sparse polynomial in coefficient form. | ||
#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)] | ||
#[derive(Clone, PartialEq, Eq, Hash, Default)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
side note: it might be a good idea to check if any other objects can be "trimmed" in a similar fashion for faster compilation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a thumbsup! rust-lang/rust#50927
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
If we're concerned with verbosity, we could always introduce helper macros in
with sth like
Such calls are cheap and they provide us with much more cross-platform safety, so even if it's an annoyance, it is IMO well worth it. |
For the record, I cannot reproduce this CI error locally: |
some CI tests checking object sizes have been flaky recently |
@vicsn the CI failures on |
I should have tackled this right away! The failing tests can be explained by the fact that we moved from In more detail:
|
1690b3b
to
8fb4a79
Compare
@vicsn can you try adding the following line to the top of #![warn(clippy::cast_possible_truncation)] We use this flag in |
Added it. For the adjustments in the last commits I:
|
@vicsn I think it's always better to have more comments to avoid any misunderstanding/misuse 👍. As for future-proofing, I think the safest course of action is to have more comprehensive tests, including extremes in terms of the number of items, but having some extra information in What we could also do is add |
let mut batch_sizes = Vec::with_capacity(batch_sizes_in.len()); | ||
for (z_b_evals, batch_size) in evaluations.z_b_evals.iter().zip(batch_sizes_in.values()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice allocation removal 👌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The further changes look fine to me 👍.
a1b5201
to
0c8a7f6
Compare
@howardwu if you like the PR's logic, I suggest we merge and I will tackle this piece by piece in the coming weeks: https://github.com/AleoHQ/snarkVM/issues/1567 |
}) | ||
} | ||
} | ||
|
||
impl<E: PairingEngine> ToBytes for CommitterKey<E> { | ||
// Values are safe to cast to u32 because they are read as u32. | ||
#[allow(clippy::cast_possible_truncation)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be fixed, not allowed. See https://github.com/AleoHQ/snarkVM/blob/testnet3/synthesizer/coinbase/src/helpers/coinbase_solution/bytes.rs#L37 for how to proceed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed: f339c4c
@@ -131,6 +131,8 @@ impl<E: PairingEngine> FromBytes for UniversalParams<E> { | |||
} | |||
} | |||
|
|||
// It is safe to cast `supported_degree_bounds.len()` and individual bounds to a `u32` because they are read and used as u32 | |||
#[allow(clippy::cast_possible_truncation)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be fixed, not allowed. See https://github.com/AleoHQ/snarkVM/blob/testnet3/synthesizer/coinbase/src/helpers/coinbase_solution/bytes.rs#L37 for how to proceed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed: f339c4c
fn update_buckets<G: AffineCurve>( | ||
base: &G, | ||
mut scalar: <G::ScalarField as PrimeField>::BigInteger, | ||
w_start: usize, | ||
c: usize, | ||
buckets: &mut [G::Projective], | ||
) { | ||
assert!(w_start <= u32::MAX as usize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for extra clarity, removed the assert now
fn batched_window<G: AffineCurve>( | ||
bases: &[G], | ||
scalars: &[<G::ScalarField as PrimeField>::BigInteger], | ||
w_start: usize, | ||
c: usize, | ||
) -> (G::Projective, usize) { | ||
assert!(w_start <= u32::MAX as usize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for extra clarity, we'd already panic if the conversion would fail so I now removed the asserts.
pub(super) fn batch_add<G: AffineCurve>( | ||
num_buckets: usize, | ||
bases: &[G], | ||
bucket_positions: &mut [BucketPosition], | ||
) -> Vec<G> { | ||
// assert_eq!(bases.len(), bucket_positions.len()); | ||
assert!(!bases.is_empty()); | ||
assert!(num_buckets <= u32::MAX as usize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So instead of panicking on a failed cast, we'd panic somewhat more clearly on this assert.
But anyway, gave it more thought, removed the assert and now passing a u32 to this function so we only have to reason about safe casting one level higher.
/// Obtain the limbs directly from a big int | ||
pub fn get_limbs_representations_from_big_integer<TargetField: PrimeField>( | ||
elem: &<TargetField as PrimeField>::BigInteger, | ||
optimization_type: OptimizationType, | ||
) -> SmallVec<[F; 10]> { | ||
let params = get_params(TargetField::size_in_bits(), F::size_in_bits(), optimization_type); | ||
assert!(params.bits_per_limb <= u32::MAX as usize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case I think it's fine to error out, or we can have params.bits_per_limb
be a u32
. Generally when we have a malformed config it's better to assert and fail quickly. This avoid polluting function signatures with Result
s
d56a16c
to
d33b4e2
Compare
Catch error in map
…d witness_labels using u32
2500e08
to
5239635
Compare
Motivation
See for a motivation this comment.
However, my initial intuition was wrong, we can't reduce developer confusion with a "use
usize
as little as possible" because Rust's library usesusize
all over the place. We instead increase clarity and safety by being more rigorous about our usage of usize. See the CONTRIBUTING doc for more details.Test Plan
Given that the main concern for this is consensus issues, I suggest to take extra care to stress test snark{VM,OS} on both 32-bit and 64-bit machines, generating test input from the different machine or constructing it manually. Let me know if you want me to help setting that up already, otherwise I'll make a TODO for once we start testing AleoBFT more extensively.