Skip to content

Commit

Permalink
feat: rust backtrace support for IronfishError
Browse files Browse the repository at this point in the history
  • Loading branch information
jowparks committed Sep 9, 2023
1 parent e39f3b4 commit 5672547
Show file tree
Hide file tree
Showing 19 changed files with 241 additions and 166 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ repository = "https://github.com/iron-fish/ironfish"

[patch.crates-io]
bellman = { git = "https://github.com/iron-fish/bellman", rev = "1cc52ca33e6db14233f1cbc0c9c5b7c822b229ec" }

[profile.release]
debug = true
13 changes: 10 additions & 3 deletions ironfish-rust/src/assets/asset.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::{errors::IronfishError, keys::PUBLIC_ADDRESS_SIZE, util::str_to_array, PublicAddress};
use crate::{
errors::{IronfishError, IronfishErrorKind},
keys::PUBLIC_ADDRESS_SIZE,
util::str_to_array,
PublicAddress,
};
use byteorder::{ReadBytesExt, WriteBytesExt};
use ironfish_zkp::constants::{ASSET_ID_LENGTH, ASSET_ID_PERSONALIZATION, GH_FIRST_BLOCK};
use jubjub::{ExtendedPoint, SubgroupPoint};
Expand Down Expand Up @@ -39,7 +44,7 @@ impl Asset {
pub fn new(creator: PublicAddress, name: &str, metadata: &str) -> Result<Asset, IronfishError> {
let trimmed_name = name.trim();
if trimmed_name.is_empty() {
return Err(IronfishError::InvalidData);
return Err(IronfishError::new(IronfishErrorKind::InvalidData));
}

let name_bytes = str_to_array(trimmed_name);
Expand All @@ -50,7 +55,9 @@ impl Asset {
if let Ok(asset) = Asset::new_with_nonce(creator, name_bytes, metadata_bytes, nonce) {
return Ok(asset);
}
nonce = nonce.checked_add(1).ok_or(IronfishError::RandomnessError)?;
nonce = nonce
.checked_add(1)
.ok_or(IronfishError::new(IronfishErrorKind::RandomnessError))?;
}
}

Expand Down
6 changes: 4 additions & 2 deletions ironfish-rust/src/assets/asset_identifier.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::errors::IronfishError;
use crate::errors::{IronfishError, IronfishErrorKind};
use group::cofactor::CofactorGroup;
use ironfish_zkp::{constants::ASSET_ID_LENGTH, util::asset_hash_to_point};
use jubjub::{ExtendedPoint, SubgroupPoint};
Expand Down Expand Up @@ -58,7 +58,9 @@ impl TryFrom<[u8; ASSET_ID_LENGTH]> for AssetIdentifier {
return Ok(Self(byte_array));
}

Err(IronfishError::InvalidAssetIdentifier)
Err(IronfishError::new(
IronfishErrorKind::InvalidAssetIdentifier,
))
}
}

Expand Down
201 changes: 119 additions & 82 deletions ironfish-rust/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,85 +2,122 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::error::Error;
use std::fmt;
use std::io;
use std::num;
use std::string;

/// Error type to handle all errors within the code and dependency-raised
/// errors. This serves 2 purposes. The first is to keep a consistent error type
/// in the code to reduce the cognitive load needed for using Result and Error
/// types. The second is to give a singular type to convert into NAPI errors to
/// be raised on the Javascript side.
#[derive(Debug)]
pub enum IronfishError {
BellpersonSynthesis(bellperson::SynthesisError),
CryptoBox(crypto_box::aead::Error),
IllegalValue,
InconsistentWitness,
InvalidAssetIdentifier,
InvalidAuthorizingKey,
InvalidBalance,
InvalidCommitment,
InvalidData,
InvalidDecryptionKey,
InvalidDiversificationPoint,
InvalidEntropy,
InvalidLanguageEncoding,
InvalidMinersFeeTransaction,
InvalidMnemonicString,
InvalidNonceLength,
InvalidNullifierDerivingKey,
InvalidPaymentAddress,
InvalidPublicAddress,
InvalidSigningKey,
InvalidTransaction,
InvalidTransactionVersion,
InvalidViewingKey,
InvalidWord,
Io(io::Error),
IsSmallOrder,
RandomnessError,
TryFromInt(num::TryFromIntError),
Utf8(string::FromUtf8Error),
VerificationFailed,
}

impl Error for IronfishError {}

impl fmt::Display for IronfishError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

impl From<io::Error> for IronfishError {
fn from(e: io::Error) -> IronfishError {
IronfishError::Io(e)
}
}

impl From<crypto_box::aead::Error> for IronfishError {
fn from(e: crypto_box::aead::Error) -> IronfishError {
IronfishError::CryptoBox(e)
}
}

impl From<string::FromUtf8Error> for IronfishError {
fn from(e: string::FromUtf8Error) -> IronfishError {
IronfishError::Utf8(e)
}
}

impl From<bellperson::SynthesisError> for IronfishError {
fn from(e: bellperson::SynthesisError) -> IronfishError {
IronfishError::BellpersonSynthesis(e)
}
}

impl From<num::TryFromIntError> for IronfishError {
fn from(e: num::TryFromIntError) -> IronfishError {
IronfishError::TryFromInt(e)
}
}
use std::backtrace::Backtrace;
use std::backtrace::BacktraceStatus;
use std::error::Error;
use std::fmt;
use std::io;
use std::num;
use std::string;

#[derive(Debug)]
pub struct IronfishError {
pub kind: IronfishErrorKind,
pub source: Option<Box<dyn Error>>,
pub backtrace: Backtrace,
}

/// Error type to handle all errors within the code and dependency-raised
/// errors. This serves 2 purposes. The first is to keep a consistent error type
/// in the code to reduce the cognitive load needed for using Result and Error
/// types. The second is to give a singular type to convert into NAPI errors to
/// be raised on the Javascript side.
#[derive(Debug)]
pub enum IronfishErrorKind {
BellpersonSynthesis,
CryptoBox,
IllegalValue,
InconsistentWitness,
InvalidAssetIdentifier,
InvalidAuthorizingKey,
InvalidBalance,
InvalidCommitment,
InvalidData,
InvalidDecryptionKey,
InvalidDiversificationPoint,
InvalidEntropy,
InvalidLanguageEncoding,
InvalidMinersFeeTransaction,
InvalidMnemonicString,
InvalidNonceLength,
InvalidNullifierDerivingKey,
InvalidPaymentAddress,
InvalidPublicAddress,
InvalidSigningKey,
InvalidTransaction,
InvalidTransactionVersion,
InvalidViewingKey,
InvalidWord,
Io,
IsSmallOrder,
RandomnessError,
TryFromInt,
Utf8,
VerificationFailed,
}

impl IronfishError {
pub fn new(kind: IronfishErrorKind) -> Self {
Self {
kind,
source: None,
backtrace: Backtrace::capture(),
}
}

pub fn new_with_source(kind: IronfishErrorKind, source: Option<Box<dyn Error>>) -> Self {
Self {
kind,
source,
backtrace: Backtrace::capture(),
}
}
}

impl Error for IronfishError {}

impl fmt::Display for IronfishError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let has_backtrace = self.backtrace.status() == BacktraceStatus::Captured;
write!(f, "{:?}", self.kind)?;
if let Some(source) = &self.source {
write!(f, "\nCaused by: \n{}", source)?;
}
if has_backtrace {
write!(f, "\nBacktrace:\n{:2.}", self.backtrace)
} else {
write!(f, "\nTo enable Rust backtraces, use RUST_BACKTRACE=1")
}
}
}

impl From<io::Error> for IronfishError {
fn from(e: io::Error) -> IronfishError {
IronfishError::new_with_source(IronfishErrorKind::Io, Some(Box::new(e)))
}
}

impl From<crypto_box::aead::Error> for IronfishError {
fn from(e: crypto_box::aead::Error) -> IronfishError {
IronfishError::new_with_source(IronfishErrorKind::CryptoBox, Some(Box::new(e)))
}
}

impl From<string::FromUtf8Error> for IronfishError {
fn from(e: string::FromUtf8Error) -> IronfishError {
IronfishError::new_with_source(IronfishErrorKind::Utf8, Some(Box::new(e)))
}
}

impl From<bellperson::SynthesisError> for IronfishError {
fn from(e: bellperson::SynthesisError) -> IronfishError {
IronfishError::new_with_source(IronfishErrorKind::BellpersonSynthesis, Some(Box::new(e)))
}
}

impl From<num::TryFromIntError> for IronfishError {
fn from(e: num::TryFromIntError) -> IronfishError {
IronfishError::new_with_source(IronfishErrorKind::TryFromInt, Some(Box::new(e)))
}
}

14 changes: 7 additions & 7 deletions ironfish-rust/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::errors::IronfishError;
use crate::errors::{IronfishError, IronfishErrorKind};
use crate::serializing::{bytes_to_hex, hex_to_bytes, read_scalar};

pub use bip39::Language;
Expand Down Expand Up @@ -81,7 +81,7 @@ impl SaplingKey {
jubjub::Fr::from_bytes_wide(&Self::convert_key(spending_key, 0));

if spend_authorizing_key == jubjub::Fr::zero() {
return Err(IronfishError::IllegalValue);
return Err(IronfishError::new(IronfishErrorKind::IllegalValue));
}

let proof_authorizing_key =
Expand Down Expand Up @@ -123,7 +123,7 @@ impl SaplingKey {
/// Load a key from a string of hexadecimal digits
pub fn from_hex(value: &str) -> Result<Self, IronfishError> {
match hex_to_bytes(value) {
Err(_) => Err(IronfishError::InvalidPaymentAddress),
Err(_) => Err(IronfishError::new(IronfishErrorKind::InvalidPaymentAddress)),
Ok(bytes) => Self::new(bytes),
}
}
Expand Down Expand Up @@ -151,7 +151,7 @@ impl SaplingKey {
pub fn write<W: io::Write>(&self, mut writer: W) -> Result<(), IronfishError> {
let num_bytes_written = writer.write(&self.spending_key)?;
if num_bytes_written != SPEND_KEY_SIZE {
return Err(IronfishError::InvalidData);
return Err(IronfishError::new(IronfishErrorKind::InvalidData));
}

Ok(())
Expand All @@ -176,13 +176,13 @@ impl SaplingKey {
/// using bip-32 and bip-39 if desired.
pub fn to_words(&self, language: Language) -> Result<Mnemonic, IronfishError> {
Mnemonic::from_entropy(&self.spending_key, language)
.map_err(|_| IronfishError::InvalidEntropy)
.map_err(|_| IronfishError::new(IronfishErrorKind::InvalidEntropy))
}

/// Takes a bip-39 phrase as a string and turns it into a SaplingKey instance
pub fn from_words(words: String, language: Language) -> Result<Self, IronfishError> {
let mnemonic = Mnemonic::from_phrase(&words, language)
.map_err(|_| IronfishError::InvalidMnemonicString)?;
.map_err(|_| IronfishError::new(IronfishErrorKind::InvalidMnemonicString))?;
let bytes = mnemonic.entropy();
let mut byte_arr = [0; SPEND_KEY_SIZE];
byte_arr.clone_from_slice(&bytes[0..SPEND_KEY_SIZE]);
Expand Down Expand Up @@ -261,7 +261,7 @@ impl SaplingKey {
// Drop the last five bits, so it can be interpreted as a scalar.
hash_result[31] &= 0b0000_0111;
if hash_result == [0; 32] {
return Err(IronfishError::InvalidViewingKey);
return Err(IronfishError::new(IronfishErrorKind::InvalidViewingKey));
}
let scalar = read_scalar(&hash_result[..])?;
Ok(scalar)
Expand Down
6 changes: 3 additions & 3 deletions ironfish-rust/src/keys/public_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::{
errors::IronfishError,
errors::{IronfishError, IronfishErrorKind},
serializing::{bytes_to_hex, hex_to_bytes},
};
use group::GroupEncoding;
Expand Down Expand Up @@ -58,7 +58,7 @@ impl PublicAddress {
/// or it fails.
pub fn from_hex(value: &str) -> Result<Self, IronfishError> {
match hex_to_bytes(value) {
Err(_) => Err(IronfishError::InvalidPublicAddress),
Err(_) => Err(IronfishError::new(IronfishErrorKind::InvalidPublicAddress)),
Ok(bytes) => Self::new(&bytes),
}
}
Expand Down Expand Up @@ -90,7 +90,7 @@ impl PublicAddress {
if transmission_key_non_prime.is_some().into() {
Ok(transmission_key_non_prime.unwrap())
} else {
Err(IronfishError::InvalidPaymentAddress)
Err(IronfishError::new(IronfishErrorKind::InvalidPaymentAddress))
}
}
}
Expand Down
Loading

0 comments on commit 5672547

Please sign in to comment.