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 7, 2023
1 parent e39f3b4 commit 27fe18f
Show file tree
Hide file tree
Showing 19 changed files with 115 additions and 84 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
6 changes: 3 additions & 3 deletions ironfish-rust/src/assets/asset.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, 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 +39,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 +50,7 @@ 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
4 changes: 2 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,7 @@ impl TryFrom<[u8; ASSET_ID_LENGTH]> for AssetIdentifier {
return Ok(Self(byte_array));
}

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

Expand Down
39 changes: 32 additions & 7 deletions ironfish-rust/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@
* 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::backtrace::Backtrace;
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 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 IronfishError {
pub enum IronfishErrorKind {
BellpersonSynthesis(bellperson::SynthesisError),
CryptoBox(crypto_box::aead::Error),
IllegalValue,
Expand Down Expand Up @@ -47,40 +54,58 @@ pub enum IronfishError {
VerificationFailed,
}

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


impl Error for IronfishError {}

impl fmt::Display for IronfishError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
let backtrace_str = self.backtrace.to_string();
let has_backtrace = !backtrace_str.contains("disabled");
write!(f, "{:?}", self.kind)?;
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::Io(e)
IronfishError::new(IronfishErrorKind::Io(e))
}
}

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

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

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

impl From<num::TryFromIntError> for IronfishError {
fn from(e: num::TryFromIntError) -> IronfishError {
IronfishError::TryFromInt(e)
IronfishError::new(IronfishErrorKind::TryFromInt(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
22 changes: 11 additions & 11 deletions ironfish-rust/src/keys/view_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use super::PublicAddress;
use crate::{
errors::IronfishError,
errors::{IronfishError, IronfishErrorKind},
serializing::{bytes_to_hex, hex_to_bytes, read_scalar},
};
use bip39::{Language, Mnemonic};
Expand Down Expand Up @@ -44,17 +44,17 @@ impl IncomingViewKey {
/// Load a key from a string of hexadecimal digits
pub fn from_hex(value: &str) -> Result<Self, IronfishError> {
match hex_to_bytes::<32>(value) {
Err(_) => Err(IronfishError::InvalidViewingKey),
Err(_) => Err(IronfishError::new(IronfishErrorKind::InvalidViewingKey)),
Ok(bytes) => Self::read(&mut bytes.as_ref()),
}
}

/// Load a key from a string of words to be decoded into bytes.
pub fn from_words(language_code: &str, value: String) -> Result<Self, IronfishError> {
let language = Language::from_language_code(language_code)
.ok_or(IronfishError::InvalidLanguageEncoding)?;
.ok_or(IronfishError::new(IronfishErrorKind::InvalidLanguageEncoding))?;
let mnemonic = Mnemonic::from_phrase(&value, language)
.map_err(|_| IronfishError::InvalidPaymentAddress)?;
.map_err(|_| IronfishError::new(IronfishErrorKind::InvalidPaymentAddress))?;
let bytes = mnemonic.entropy();
let mut byte_arr = [0; 32];
byte_arr.clone_from_slice(&bytes[0..32]);
Expand All @@ -69,7 +69,7 @@ impl IncomingViewKey {
/// Even more readable
pub fn words_key(&self, language_code: &str) -> Result<String, IronfishError> {
let language = Language::from_language_code(language_code)
.ok_or(IronfishError::InvalidLanguageEncoding)?;
.ok_or(IronfishError::new(IronfishErrorKind::InvalidLanguageEncoding))?;
let mnemonic = Mnemonic::from_entropy(&self.view_key.to_bytes(), language).unwrap();
Ok(mnemonic.phrase().to_string())
}
Expand Down Expand Up @@ -113,10 +113,10 @@ impl ViewKey {
nullifier_deriving_key_bytes.clone_from_slice(&bytes[32..]);

let authorizing_key = Option::from(SubgroupPoint::from_bytes(&authorizing_key_bytes))
.ok_or(IronfishError::InvalidAuthorizingKey)?;
.ok_or(IronfishError::new(IronfishErrorKind::InvalidAuthorizingKey))?;
let nullifier_deriving_key =
Option::from(SubgroupPoint::from_bytes(&nullifier_deriving_key_bytes))
.ok_or(IronfishError::InvalidNullifierDerivingKey)?;
.ok_or(IronfishError::new(IronfishErrorKind::InvalidNullifierDerivingKey))?;

Ok(Self {
authorizing_key,
Expand Down Expand Up @@ -149,17 +149,17 @@ impl OutgoingViewKey {
/// 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::InvalidViewingKey),
Err(_) => Err(IronfishError::new(IronfishErrorKind::InvalidViewingKey)),
Ok(bytes) => Ok(Self { view_key: bytes }),
}
}

/// Load a key from a string of words to be decoded into bytes.
pub fn from_words(language_code: &str, value: String) -> Result<Self, IronfishError> {
let language = Language::from_language_code(language_code)
.ok_or(IronfishError::InvalidLanguageEncoding)?;
.ok_or(IronfishError::new(IronfishErrorKind::InvalidLanguageEncoding))?;
let mnemonic = Mnemonic::from_phrase(&value, language)
.map_err(|_| IronfishError::InvalidPaymentAddress)?;
.map_err(|_| IronfishError::new(IronfishErrorKind::InvalidPaymentAddress))?;
let bytes = mnemonic.entropy();
let mut view_key = [0; 32];
view_key.clone_from_slice(&bytes[0..32]);
Expand All @@ -174,7 +174,7 @@ impl OutgoingViewKey {
/// Even more readable
pub fn words_key(&self, language_code: &str) -> Result<String, IronfishError> {
let language = Language::from_language_code(language_code)
.ok_or(IronfishError::InvalidLanguageEncoding)?;
.ok_or(IronfishError::new(IronfishErrorKind::InvalidLanguageEncoding))?;
let mnemonic = Mnemonic::from_entropy(&self.view_key, language).unwrap();
Ok(mnemonic.phrase().to_string())
}
Expand Down
4 changes: 2 additions & 2 deletions ironfish-rust/src/nacl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crypto_box::{
};
use rand::RngCore;

use crate::errors::IronfishError;
use crate::errors::{IronfishError, IronfishErrorKind};

pub const KEY_LENGTH: usize = crypto_box::KEY_SIZE;
pub const NONCE_LENGTH: usize = 24;
Expand Down Expand Up @@ -57,7 +57,7 @@ pub fn unbox_message(
recipient_secret_key: [u8; KEY_LENGTH],
) -> Result<String, IronfishError> {
if nonce.len() != NONCE_LENGTH {
return Err(IronfishError::InvalidNonceLength);
return Err(IronfishError::new(IronfishErrorKind::InvalidNonceLength))
}

let nonce = GenericArray::from_slice(nonce);
Expand Down
4 changes: 2 additions & 2 deletions ironfish-rust/src/note.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::{
assets::asset_identifier::AssetIdentifier, errors::IronfishError, keys::PUBLIC_ADDRESS_SIZE,
assets::asset_identifier::AssetIdentifier, errors::{IronfishError, IronfishErrorKind}, keys::PUBLIC_ADDRESS_SIZE,
util::str_to_array, ViewKey,
};

Expand Down Expand Up @@ -330,7 +330,7 @@ impl<'a> Note {
if commitment == self.commitment_point() {
Ok(())
} else {
Err(IronfishError::InvalidCommitment)
Err(IronfishError::new(IronfishErrorKind::InvalidCommitment))
}
}

Expand Down
6 changes: 3 additions & 3 deletions ironfish-rust/src/serializing/aead.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 chacha20poly1305::aead::{AeadInPlace, NewAead};
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};

Expand All @@ -28,7 +28,7 @@ pub(crate) fn encrypt<const SIZE: usize>(
&[],
&mut encrypted_output[..plaintext.len()],
)
.map_err(|_| IronfishError::InvalidSigningKey)?;
.map_err(|_| IronfishError::new(IronfishErrorKind::InvalidSigningKey))?;
encrypted_output[plaintext.len()..].copy_from_slice(&tag);

Ok(encrypted_output)
Expand All @@ -53,7 +53,7 @@ pub(crate) fn decrypt<const SIZE: usize>(
&mut plaintext,
ciphertext[SIZE..].into(),
)
.map_err(|_| IronfishError::InvalidDecryptionKey)?;
.map_err(|_| IronfishError::new(IronfishErrorKind::InvalidDecryptionKey))?;

Ok(plaintext)
}
Expand Down
Loading

0 comments on commit 27fe18f

Please sign in to comment.