Skip to content

Commit bf23fbf

Browse files
authored
Merge pull request #78 from iron-fish/feat/hughy/ironfish-frost-error-fmt
adds error formatting for IronfishFrostError if std is enabled
2 parents 26012b0 + 7892b58 commit bf23fbf

File tree

5 files changed

+158
-22
lines changed

5 files changed

+158
-22
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ hex-literal = "0.4.1"
2424
rand = "0.8.5"
2525

2626
[features]
27-
default = ["signing"]
27+
default = ["dkg", "std"]
2828

29-
std = []
29+
std = ["reddsa/std"]
3030
signing = ["dep:blake3", "dep:rand_chacha", "std"]
3131
dkg = []

src/dkg/round1.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ extern crate alloc;
3737
#[cfg(not(feature = "std"))]
3838
use alloc::vec::Vec;
3939

40+
#[cfg(not(feature = "std"))]
41+
use alloc::string::ToString;
42+
4043
type Scalar = <JubjubScalarField as Field>::Scalar;
4144

4245
/// Copy of the [`frost_core::dkg::round1::SecretPackage`] struct. Necessary to implement
@@ -160,7 +163,8 @@ pub fn import_secret_package(
160163
exported: &[u8],
161164
secret: &participant::Secret,
162165
) -> Result<SecretPackage, IronfishFrostError> {
163-
let serialized = multienc::decrypt(secret, exported).map_err(io::Error::other)?;
166+
let serialized =
167+
multienc::decrypt(secret, exported).map_err(IronfishFrostError::DecryptionError)?;
164168
SerializableSecretPackage::deserialize_from(&serialized[..]).map(|pkg| pkg.into())
165169
}
166170

@@ -300,11 +304,13 @@ where
300304
let participants = participants;
301305

302306
if !participants.contains(&self_identity) {
303-
return Err(IronfishFrostError::InvalidInput);
307+
return Err(IronfishFrostError::InvalidInput(
308+
"participants must include self_identity".to_string(),
309+
));
304310
}
305311

306-
let max_signers =
307-
u16::try_from(participants.len()).map_err(|_| IronfishFrostError::InvalidInput)?;
312+
let max_signers = u16::try_from(participants.len())
313+
.map_err(|_| IronfishFrostError::InvalidInput("too many participants".to_string()))?;
308314

309315
let (secret_package, public_package) = frost::keys::dkg::part1(
310316
self_identity.to_frost_identifier(),
@@ -314,7 +320,8 @@ where
314320
)?;
315321

316322
let encrypted_secret_package =
317-
export_secret_package(&secret_package, self_identity, &mut csrng)?;
323+
export_secret_package(&secret_package, self_identity, &mut csrng)
324+
.map_err(IronfishFrostError::EncryptionError)?;
318325

319326
let group_secret_key_shard = GroupSecretKeyShard::random(&mut csrng);
320327

src/dkg/round2.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ extern crate alloc;
4141
#[cfg(not(feature = "std"))]
4242
use alloc::collections::BTreeMap;
4343
#[cfg(not(feature = "std"))]
44+
use alloc::string::ToString;
45+
#[cfg(not(feature = "std"))]
4446
use alloc::vec::Vec;
4547

4648
type Scalar = <JubjubScalarField as Field>::Scalar;
@@ -163,7 +165,8 @@ pub fn import_secret_package(
163165
exported: &[u8],
164166
secret: &participant::Secret,
165167
) -> Result<SecretPackage, IronfishFrostError> {
166-
let serialized = multienc::decrypt(secret, exported).map_err(io::Error::other)?;
168+
let serialized =
169+
multienc::decrypt(secret, exported).map_err(IronfishFrostError::DecryptionError)?;
167170
SerializableSecretPackage::deserialize_from(&serialized[..]).map(|pkg| pkg.into())
168171
}
169172

@@ -377,7 +380,17 @@ where
377380

378381
// Ensure that the number of public packages provided matches max_signers
379382
if round1_public_packages.len() != max_signers as usize {
380-
return Err(IronfishFrostError::InvalidInput);
383+
#[cfg(feature = "std")]
384+
return Err(IronfishFrostError::InvalidInput(format!(
385+
"expected {} public packages, got {}",
386+
max_signers,
387+
round1_public_packages.len()
388+
)));
389+
390+
#[cfg(not(feature = "std"))]
391+
return Err(IronfishFrostError::InvalidInput(
392+
"incorrect number of round 1 public packages".to_string(),
393+
));
381394
}
382395

383396
let expected_round1_checksum = round1::input_checksum(
@@ -402,7 +415,16 @@ where
402415
.insert(frost_identifier, frost_package)
403416
.is_some()
404417
{
405-
return Err(IronfishFrostError::InvalidInput);
418+
#[cfg(feature = "std")]
419+
return Err(IronfishFrostError::InvalidInput(format!(
420+
"multiple public packages provided for identity {}",
421+
public_package.identity()
422+
)));
423+
424+
#[cfg(not(feature = "std"))]
425+
return Err(IronfishFrostError::InvalidInput(
426+
"multiple public packages provided for an identity".to_string(),
427+
));
406428
}
407429

408430
identities.insert(frost_identifier, identity);
@@ -428,7 +450,8 @@ where
428450

429451
// Encrypt the secret package
430452
let encrypted_secret_package =
431-
export_secret_package(&round2_secret_package, &self_identity, &mut csrng)?;
453+
export_secret_package(&round2_secret_package, &self_identity, &mut csrng)
454+
.map_err(IronfishFrostError::EncryptionError)?;
432455

433456
// Convert the Identifier->Package map to an Identity->PublicPackage map
434457
let mut round2_public_packages = Vec::new();
@@ -690,7 +713,7 @@ mod tests {
690713
);
691714

692715
match result {
693-
Err(IronfishFrostError::InvalidInput) => (),
716+
Err(IronfishFrostError::InvalidInput(_)) => (),
694717
_ => panic!("dkg round2 should have failed with InvalidInput"),
695718
}
696719
}
@@ -718,7 +741,7 @@ mod tests {
718741

719742
// We can use `assert_matches` once it's stabilized
720743
match result {
721-
Err(IronfishFrostError::InvalidInput) => (),
744+
Err(IronfishFrostError::InvalidInput(_)) => (),
722745
_ => panic!("dkg round2 should have failed with InvalidInput"),
723746
}
724747
}

src/dkg/round3.rs

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ extern crate alloc;
3232
#[cfg(not(feature = "std"))]
3333
use alloc::collections::BTreeMap;
3434
#[cfg(not(feature = "std"))]
35+
use alloc::string::ToString;
36+
#[cfg(not(feature = "std"))]
3537
use alloc::vec::Vec;
3638

3739
#[derive(Clone, Eq, PartialEq, Debug)]
@@ -134,12 +136,32 @@ where
134136
// Ensure that the number of public packages provided matches max_signers
135137
let expected_round1_packages = max_signers as usize;
136138
if round1_public_packages.len() != expected_round1_packages {
137-
return Err(IronfishFrostError::InvalidInput);
139+
#[cfg(feature = "std")]
140+
return Err(IronfishFrostError::InvalidInput(format!(
141+
"expected {} round 1 public packages, got {}",
142+
expected_round1_packages,
143+
round1_public_packages.len()
144+
)));
145+
146+
#[cfg(not(feature = "std"))]
147+
return Err(IronfishFrostError::InvalidInput(
148+
"incorrect number of round 1 public packages".to_string(),
149+
));
138150
}
139151

140152
let expected_round2_packages = expected_round1_packages.saturating_sub(1);
141153
if round2_public_packages.len() != expected_round2_packages {
142-
return Err(IronfishFrostError::InvalidInput);
154+
#[cfg(feature = "std")]
155+
return Err(IronfishFrostError::InvalidInput(format!(
156+
"expected {} round 2 public packages, got {}",
157+
expected_round2_packages,
158+
round2_public_packages.len()
159+
)));
160+
161+
#[cfg(not(feature = "std"))]
162+
return Err(IronfishFrostError::InvalidInput(
163+
"incorrect number of round 2 public packages".to_string(),
164+
));
143165
}
144166

145167
let expected_round1_checksum = round1::input_checksum(
@@ -166,10 +188,21 @@ where
166188
.insert(frost_identifier, frost_package)
167189
.is_some()
168190
{
169-
return Err(IronfishFrostError::InvalidInput);
191+
#[cfg(feature = "std")]
192+
return Err(IronfishFrostError::InvalidInput(format!(
193+
"multiple round 1 public packages provided for identity {}",
194+
public_package.identity()
195+
)));
196+
197+
#[cfg(not(feature = "std"))]
198+
return Err(IronfishFrostError::InvalidInput(
199+
"multiple round 1 public packages provided for an identity".to_string(),
200+
));
170201
}
171202

172-
let gsk_shard = public_package.group_secret_key_shard(secret)?;
203+
let gsk_shard = public_package
204+
.group_secret_key_shard(secret)
205+
.map_err(IronfishFrostError::DecryptionError)?;
173206
gsk_shards.push(gsk_shard);
174207
identities.push(identity.clone());
175208
}
@@ -181,7 +214,9 @@ where
181214
// inputs
182215
round1_frost_packages
183216
.remove(&identity.to_frost_identifier())
184-
.ok_or(IronfishFrostError::InvalidInput)?;
217+
.ok_or(IronfishFrostError::InvalidInput(
218+
"missing round 1 public package for own identity".to_string(),
219+
))?;
185220

186221
let expected_round2_checksum =
187222
round2::input_checksum(round1_public_packages.iter().map(Borrow::borrow));
@@ -195,7 +230,16 @@ where
195230
}
196231

197232
if !identity.eq(public_package.recipient_identity()) {
198-
return Err(IronfishFrostError::InvalidInput);
233+
#[cfg(feature = "std")]
234+
return Err(IronfishFrostError::InvalidInput(format!(
235+
"round 2 public package does not have the correct recipient identity {:?}",
236+
public_package.recipient_identity().serialize()
237+
)));
238+
239+
#[cfg(not(feature = "std"))]
240+
return Err(IronfishFrostError::InvalidInput(
241+
"round 2 public package does not have the correct recipient identity".to_string(),
242+
));
199243
}
200244

201245
let frost_identifier = public_package.sender_identity().to_frost_identifier();
@@ -205,7 +249,16 @@ where
205249
.insert(frost_identifier, frost_package)
206250
.is_some()
207251
{
208-
return Err(IronfishFrostError::InvalidInput);
252+
#[cfg(feature = "std")]
253+
return Err(IronfishFrostError::InvalidInput(format!(
254+
"multiple round 2 public packages provided for identity {}",
255+
public_package.sender_identity(),
256+
)));
257+
258+
#[cfg(not(feature = "std"))]
259+
return Err(IronfishFrostError::InvalidInput(
260+
"multiple round 2 public packages provided for an identity".to_string(),
261+
));
209262
}
210263
}
211264

@@ -334,7 +387,7 @@ mod tests {
334387
);
335388

336389
match result {
337-
Err(IronfishFrostError::InvalidInput) => (),
390+
Err(IronfishFrostError::InvalidInput(_)) => (),
338391
_ => panic!("dkg round3 should have failed with InvalidInput"),
339392
}
340393
}

src/error.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@ use crate::io;
99

1010
use crate::checksum::ChecksumError;
1111

12+
#[cfg(not(feature = "std"))]
13+
extern crate alloc;
14+
#[cfg(not(feature = "std"))]
15+
use alloc::string::String;
16+
1217
#[derive(Debug)]
1318
pub enum IronfishFrostError {
14-
InvalidInput,
19+
InvalidInput(String),
1520
StdError,
1621
IoError(io::Error),
22+
DecryptionError(io::Error),
23+
EncryptionError(io::Error),
1724
FrostError(FrostError<JubjubBlake2b512>),
1825
SignatureError(ed25519_dalek::SignatureError),
1926
ChecksumError(ChecksumError),
@@ -36,3 +43,49 @@ impl From<ed25519_dalek::SignatureError> for IronfishFrostError {
3643
IronfishFrostError::SignatureError(error)
3744
}
3845
}
46+
47+
#[cfg(feature = "std")]
48+
use std::fmt;
49+
50+
#[cfg(feature = "std")]
51+
impl fmt::Display for IronfishFrostError {
52+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
53+
match self {
54+
Self::InvalidInput(e) => {
55+
write!(f, "invalid input: ")?;
56+
e.fmt(f)
57+
}
58+
Self::StdError => {
59+
write!(f, "std error")?;
60+
Ok(())
61+
}
62+
Self::IoError(e) => {
63+
write!(f, "io error: ")?;
64+
e.fmt(f)
65+
}
66+
Self::DecryptionError(e) => {
67+
write!(f, "decryption error: ")?;
68+
e.fmt(f)
69+
}
70+
Self::EncryptionError(e) => {
71+
write!(f, "encryption error: ")?;
72+
e.fmt(f)
73+
}
74+
Self::FrostError(e) => {
75+
write!(f, "frost error: ")?;
76+
e.fmt(f)
77+
}
78+
Self::SignatureError(e) => {
79+
write!(f, "signature rror: ")?;
80+
e.fmt(f)
81+
}
82+
Self::ChecksumError(e) => {
83+
write!(f, "checksum error: ")?;
84+
e.fmt(f)
85+
}
86+
}
87+
}
88+
}
89+
90+
#[cfg(feature = "std")]
91+
impl std::error::Error for IronfishFrostError {}

0 commit comments

Comments
 (0)