Skip to content

Commit

Permalink
Improve parameter error messages (tari-project#106)
Browse files Browse the repository at this point in the history
This PR adds more detailed messages to parameter errors.

Closes tari-project#77. Supersedes tari-project#76.
  • Loading branch information
AaronFeickert authored Aug 11, 2024
1 parent 3299482 commit a0760a0
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 101 deletions.
33 changes: 25 additions & 8 deletions src/parallel/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ pub struct TriptychParameters {
#[derive(Debug, Snafu)]
pub enum ParameterError {
/// An invalid parameter was provided.
#[snafu(display("An invalid parameter was provided"))]
InvalidParameter,
#[snafu(display("An invalid parameter was provided: {reason}"))]
InvalidParameter {
/// The reason for the parameter error.
reason: &'static str,
},
}

impl TriptychParameters {
Expand Down Expand Up @@ -98,13 +101,18 @@ impl TriptychParameters {
U: &RistrettoPoint,
) -> Result<Self, ParameterError> {
// These bounds are required by the protocol
if n < 2 || m < 2 {
return Err(ParameterError::InvalidParameter);
if n < 2 {
return Err(ParameterError::InvalidParameter { reason: "`n < 2`" });
}
if m < 2 {
return Err(ParameterError::InvalidParameter { reason: "`m < 2`" });
}

// Check that the parameters don't overflow `u32`
if n.checked_pow(m).is_none() {
return Err(ParameterError::InvalidParameter);
return Err(ParameterError::InvalidParameter {
reason: "`n**m` overflowed `u32`",
});
}

// Use `BLAKE3` to generate `CommitmentH`
Expand All @@ -121,7 +129,9 @@ impl TriptychParameters {
hasher.update(&m.to_le_bytes());
let mut hasher_xof = hasher.finalize_xof();
let mut CommitmentG_bytes = [0u8; 64];
let CommitmentG = (0..n.checked_mul(m).ok_or(ParameterError::InvalidParameter)?)
let CommitmentG = (0..n.checked_mul(m).ok_or(ParameterError::InvalidParameter {
reason: "`n*m` overflowed `u32`",
})?)
.map(|_| {
hasher_xof.fill(&mut CommitmentG_bytes);
RistrettoPoint::from_uniform_bytes(&CommitmentG_bytes)
Expand Down Expand Up @@ -166,8 +176,15 @@ impl TriptychParameters {
timing: OperationTiming,
) -> Result<RistrettoPoint, ParameterError> {
// Check that the matrix dimensions are valid
if matrix.len() != (self.m as usize) || matrix.iter().any(|m| m.len() != (self.n as usize)) {
return Err(ParameterError::InvalidParameter);
if matrix.len() != (self.m as usize) {
return Err(ParameterError::InvalidParameter {
reason: "matrix did not have `m` rows",
});
}
if matrix.iter().any(|m| m.len() != (self.n as usize)) {
return Err(ParameterError::InvalidParameter {
reason: "matrix did not have `n` columns",
});
}

// Flatten before evaluating the commitment
Expand Down
114 changes: 85 additions & 29 deletions src/parallel/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ pub struct TriptychProof {
#[derive(Debug, Snafu)]
pub enum ProofError {
/// An invalid parameter was provided.
#[snafu(display("An invalid parameter was provided"))]
InvalidParameter,
#[snafu(display("An invalid parameter was provided: {reason}"))]
InvalidParameter {
/// The reason for the parameter error.
reason: &'static str,
},
/// A transcript challenge was invalid.
#[snafu(display("A transcript challenge was invalid"))]
InvalidChallenge,
Expand Down Expand Up @@ -174,7 +177,9 @@ impl TriptychProof {
) -> Result<Self, ProofError> {
// Check that the witness and statement have identical parameters
if witness.get_params() != statement.get_params() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "witness and statement parameters did not match",
});
}

// Extract values for convenience
Expand Down Expand Up @@ -205,13 +210,17 @@ impl TriptychProof {
}

if M_l != r * params.get_G() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "`M[l] != r * G`",
});
}
if M1_l - offset != r1 * params.get_G1() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "`M1[l] - offset != r1 * G1`",
});
}
if &(r * J) != params.get_U() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter { reason: "`r * J != U`" });
}

// Set up the transcript
Expand All @@ -231,16 +240,23 @@ impl TriptychProof {
}
let A = params
.commit_matrix(&a, &r_A, timing)
.map_err(|_| ProofError::InvalidParameter)?;
.map_err(|_| ProofError::InvalidParameter {
reason: "unable to compute `A`",
})?;

// Compute the `B` matrix commitment
let r_B = Scalar::random(transcript.as_mut_rng());
let l_decomposed = match timing {
OperationTiming::Constant => {
GrayIterator::decompose(params.get_n(), params.get_m(), l).ok_or(ProofError::InvalidParameter)?
GrayIterator::decompose(params.get_n(), params.get_m(), l).ok_or(ProofError::InvalidParameter {
reason: "`l` decomposition failed",
})?
},
OperationTiming::Variable => GrayIterator::decompose_vartime(params.get_n(), params.get_m(), l)
.ok_or(ProofError::InvalidParameter)?,
OperationTiming::Variable => GrayIterator::decompose_vartime(params.get_n(), params.get_m(), l).ok_or(
ProofError::InvalidParameter {
reason: "`l` decomposition failed",
},
)?,
};
let sigma = (0..params.get_m())
.map(|j| {
Expand All @@ -251,7 +267,9 @@ impl TriptychProof {
.collect::<Vec<Vec<Scalar>>>();
let B = params
.commit_matrix(&sigma, &r_B, timing)
.map_err(|_| ProofError::InvalidParameter)?;
.map_err(|_| ProofError::InvalidParameter {
reason: "unable to compute `B`",
})?;

// Compute the `C` matrix commitment
let two = Scalar::from(2u32);
Expand All @@ -265,7 +283,9 @@ impl TriptychProof {
.collect::<Vec<Vec<Scalar>>>();
let C = params
.commit_matrix(&a_sigma, &r_C, timing)
.map_err(|_| ProofError::InvalidParameter)?;
.map_err(|_| ProofError::InvalidParameter {
reason: "unable to compute `C`",
})?;

// Compute the `D` matrix commitment
let r_D = Scalar::random(transcript.as_mut_rng());
Expand All @@ -278,7 +298,9 @@ impl TriptychProof {
.collect::<Vec<Vec<Scalar>>>();
let D = params
.commit_matrix(&a_square, &r_D, timing)
.map_err(|_| ProofError::InvalidParameter)?;
.map_err(|_| ProofError::InvalidParameter {
reason: "unable to compute `D`",
})?;

// Random masks
let rho = Zeroizing::new(
Expand All @@ -296,7 +318,9 @@ impl TriptychProof {
let mut p = Vec::<Vec<Scalar>>::with_capacity(params.get_N() as usize);
let mut k_decomposed = vec![0; params.get_m() as usize];
for (gray_index, _, gray_new) in
GrayIterator::new(params.get_n(), params.get_m()).ok_or(ProofError::InvalidParameter)?
GrayIterator::new(params.get_n(), params.get_m()).ok_or(ProofError::InvalidParameter {
reason: "coefficient decomposition failed",
})?
{
k_decomposed[gray_index] = gray_new;

Expand All @@ -305,7 +329,9 @@ impl TriptychProof {
coefficients.resize(
(params.get_m() as usize)
.checked_add(1)
.ok_or(ProofError::InvalidParameter)?,
.ok_or(ProofError::InvalidParameter {
reason: "polynomial degree overflowed",
})?,
Scalar::ZERO,
);
coefficients[0] = a[0][k_decomposed[0] as usize];
Expand Down Expand Up @@ -564,10 +590,14 @@ impl TriptychProof {
) -> Result<(), ProofError> {
// Check that we have the same number of statements, proofs, and transcripts
if statements.len() != proofs.len() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "number of statements and proofs does not match",
});
}
if statements.len() != transcripts.len() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "number of statements and transcripts does not match",
});
}

// An empty batch is considered trivially valid
Expand All @@ -578,12 +608,16 @@ impl TriptychProof {

// Each statement must use the same input set (checked using the hash for efficiency)
if !statements.iter().map(|s| s.get_input_set().get_hash()).all_equal() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "statement input sets do not match",
});
}

// Each statement must use the same parameters (checked using the hash for efficiency)
if !statements.iter().map(|s| s.get_params().get_hash()).all_equal() {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "statement parameters do not match",
});
}

// Extract common values for convenience
Expand All @@ -594,26 +628,42 @@ impl TriptychProof {
// Check that all proof semantics are valid for the statement
for proof in proofs {
if proof.X.len() != params.get_m() as usize {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "proof `X` vector length was not `m`",
});
}
if proof.X1.len() != params.get_m() as usize {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "proof `X1` vector length was not `m`",
});
}
if proof.Y.len() != params.get_m() as usize {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "proof `Y` vector length was not `m`",
});
}
if proof.f.len() != params.get_m() as usize {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "proof `f` matrix did not have `m` rows",
});
}
for f_row in &proof.f {
if f_row.len() != params.get_n().checked_sub(1).ok_or(ProofError::InvalidParameter)? as usize {
return Err(ProofError::InvalidParameter);
if f_row.len() !=
params.get_n().checked_sub(1).ok_or(ProofError::InvalidParameter {
reason: "proof `f` matrix column count overflowed",
})? as usize
{
return Err(ProofError::InvalidParameter {
reason: "proof `f` matrix did not have `n - 1` columns",
});
}
}
}

// Determine the size of the final check vector, which must not overflow `usize`
let batch_size = u32::try_from(proofs.len()).map_err(|_| ProofError::InvalidParameter)?;
let batch_size = u32::try_from(proofs.len()).map_err(|_| ProofError::InvalidParameter {
reason: "batch size overflowed `u32`",
})?;

// This is unlikely to overflow; even if it does, the only effect is unnecessary reallocation
#[allow(clippy::arithmetic_side_effects)]
Expand All @@ -631,7 +681,9 @@ impl TriptychProof {
+ 3 * params.get_m() // X, X1, Y
),
)
.map_err(|_| ProofError::InvalidParameter)?;
.map_err(|_| ProofError::InvalidParameter {
reason: "multiscalar multiplication size overflowed `usize`",
})?;

// Set up the point vector for the final check
let points = proofs
Expand Down Expand Up @@ -708,7 +760,9 @@ impl TriptychProof {
// Check that `f` does not contain zero, which breaks batch inversion
for f_row in &f {
if f_row.contains(&Scalar::ZERO) {
return Err(ProofError::InvalidParameter);
return Err(ProofError::InvalidParameter {
reason: "proof `f` matrix contained 0",
});
}
}

Expand Down Expand Up @@ -779,7 +833,9 @@ impl TriptychProof {
// Set up the initial `f` product and Gray iterator
let mut f_product = f.iter().map(|f_row| f_row[0]).product::<Scalar>();
let gray_iterator =
GrayIterator::new(params.get_n(), params.get_m()).ok_or(ProofError::InvalidParameter)?;
GrayIterator::new(params.get_n(), params.get_m()).ok_or(ProofError::InvalidParameter {
reason: "coefficient decomposition failed",
})?;

// Invert each element of `f` for efficiency
let mut f_inverse_flat = f.iter().flatten().copied().collect::<Vec<Scalar>>();
Expand Down
Loading

0 comments on commit a0760a0

Please sign in to comment.