Skip to content
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

feat: rust std::backtrace #4274

Merged
merged 2 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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" }

jowparks marked this conversation as resolved.
Show resolved Hide resolved
[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_else(|| 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
70 changes: 57 additions & 13 deletions ironfish-rust/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,30 @@
* 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::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,
jowparks marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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),
pub enum IronfishErrorKind {
BellpersonSynthesis,
CryptoBox,
IllegalValue,
InconsistentWitness,
InvalidAssetIdentifier,
Expand All @@ -29,58 +38,93 @@ pub enum IronfishError {
InvalidEntropy,
InvalidLanguageEncoding,
InvalidMinersFeeTransaction,
InvalidMintProof,
InvalidMintSignature,
InvalidMnemonicString,
InvalidNonceLength,
InvalidNullifierDerivingKey,
InvalidOutputProof,
InvalidPaymentAddress,
InvalidPublicAddress,
InvalidSignature,
InvalidSigningKey,
InvalidSpendProof,
InvalidSpendSignature,
InvalidTransaction,
InvalidTransactionVersion,
InvalidViewingKey,
InvalidWord,
Io(io::Error),
Io,
IsSmallOrder,
RandomnessError,
TryFromInt(num::TryFromIntError),
Utf8(string::FromUtf8Error),
VerificationFailed,
TryFromInt,
Utf8,
}

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

pub fn new_with_source<E>(kind: IronfishErrorKind, source: E) -> Self
where
E: Into<Box<dyn Error>>,
{
Self {
kind,
source: Some(source.into()),
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 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::Io(e)
IronfishError::new_with_source(IronfishErrorKind::Io, e)
}
}

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

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

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

impl From<num::TryFromIntError> for IronfishError {
fn from(e: num::TryFromIntError) -> IronfishError {
IronfishError::TryFromInt(e)
IronfishError::new_with_source(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
27 changes: 14 additions & 13 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_else(|| 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_else(|| 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,11 @@ 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)?;
let nullifier_deriving_key =
Option::from(SubgroupPoint::from_bytes(&nullifier_deriving_key_bytes))
.ok_or(IronfishError::InvalidNullifierDerivingKey)?;
.ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidAuthorizingKey))?;
let nullifier_deriving_key = Option::from(SubgroupPoint::from_bytes(
&nullifier_deriving_key_bytes,
))
.ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidNullifierDerivingKey))?;

Ok(Self {
authorizing_key,
Expand Down Expand Up @@ -149,17 +150,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_else(|| 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 +175,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_else(|| 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
Loading