diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index fed511e4054..653fbf0c135 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -15,24 +15,17 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: - # Work around https://github.com/actions/runner-images/issues/8659 - - name: Remove GCC 13 from runner image - shell: bash - run: | - sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list - sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | - tools/install-sys-dependencies-linux + tools/install-sys-dependencies-linux ci tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v3 with: path: build/local - key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-sys-dependencies-linux') }}-${{ hashFiles('tools/install-dependencies') }} - name: Install internal dependencies run: | tools/install-dependencies diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index 580351d8ca8..da1aa11656b 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -15,24 +15,17 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: - # Work around https://github.com/actions/runner-images/issues/8659 - - name: Remove GCC 13 from runner image - shell: bash - run: | - sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list - sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | - tools/install-sys-dependencies-linux + tools/install-sys-dependencies-linux ci tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v3 with: path: build/local - key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-sys-dependencies-linux') }}-${{ hashFiles('tools/install-dependencies') }} - name: Install internal dependencies run: | tools/install-dependencies diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs index 4ca837b12fb..3ec4f8516b5 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs @@ -5,7 +5,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; pub struct {BLOCKCHAIN}Address { diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs index aff08a27680..63d51f1df50 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs @@ -4,7 +4,7 @@ use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::{BLOCKCHAIN}::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs index 0cb80268185..c76b1b25d69 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs index 343b819b63f..3a64018e599 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::{BLOCKCHAIN}::Proto; diff --git a/rust/chains/tw_aptos/src/address.rs b/rust/chains/tw_aptos/src/address.rs index 520166c9798..3915750c625 100644 --- a/rust/chains/tw_aptos/src/address.rs +++ b/rust/chains/tw_aptos/src/address.rs @@ -6,7 +6,7 @@ use move_core_types::account_address::{AccountAddress, AccountAddressParseError} use std::fmt::{Display, Formatter}; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_hash::sha3::sha3_256; use tw_keypair::ed25519; use tw_memory::Data; diff --git a/rust/chains/tw_aptos/src/aptos_move_packages.rs b/rust/chains/tw_aptos/src/aptos_move_packages.rs index 3d3d318cc0d..e90e6bf2a4b 100644 --- a/rust/chains/tw_aptos/src/aptos_move_packages.rs +++ b/rust/chains/tw_aptos/src/aptos_move_packages.rs @@ -7,7 +7,7 @@ use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::json; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; pub fn aptos_account_transfer( diff --git a/rust/chains/tw_aptos/src/bcs_encoding.rs b/rust/chains/tw_aptos/src/bcs_encoding.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/rust/chains/tw_aptos/src/compiler.rs b/rust/chains/tw_aptos/src/compiler.rs index 73954261f19..53b9df11bd4 100644 --- a/rust/chains/tw_aptos/src/compiler.rs +++ b/rust/chains/tw_aptos/src/compiler.rs @@ -2,7 +2,7 @@ use crate::address::Address; use crate::transaction_builder; use std::str::FromStr; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -22,7 +22,9 @@ impl Compiler { input: Proto::SigningInput<'_>, ) -> SigningResult> { let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; - let sender = Address::from_str(&input.sender)?; + let sender = Address::from_str(&input.sender) + .into_tw() + .context("Invalid sender address")?; let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) @@ -53,10 +55,10 @@ impl Compiler { let sender = Address::from_str(&input.sender)?; let signature = signatures .first() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count)?; let public_key = public_keys .first() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count)?; let signed_tx = builder .sender(sender.inner()) diff --git a/rust/chains/tw_aptos/src/entry.rs b/rust/chains/tw_aptos/src/entry.rs index 741502bdf93..bee4fb47ddc 100644 --- a/rust/chains/tw_aptos/src/entry.rs +++ b/rust/chains/tw_aptos/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_aptos/src/liquid_staking.rs b/rust/chains/tw_aptos/src/liquid_staking.rs index 4b1027abe9e..2ce311e4350 100644 --- a/rust/chains/tw_aptos/src/liquid_staking.rs +++ b/rust/chains/tw_aptos/src/liquid_staking.rs @@ -7,7 +7,7 @@ use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; use serde_json::json; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; use tw_proto::{ Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, @@ -91,7 +91,9 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::stake(stake_msg) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid Smart Contract address")?; Ok(LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, smart_contract_address, @@ -100,7 +102,9 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid Smart Contract address")?; Ok(LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, smart_contract_address, @@ -109,14 +113,16 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::claim(claim) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid Smart Contract address")?; Ok(LiquidStakingOperation::Claim(Claim { idx: claim.idx, smart_contract_address, })) }, OneOfliquid_stake_transaction_payload::None => { - Err(SigningError(SigningErrorType::Error_invalid_params)) + SigningError::err(SigningErrorType::Error_invalid_params) }, } } diff --git a/rust/chains/tw_aptos/src/nft.rs b/rust/chains/tw_aptos/src/nft.rs index caf46b432bb..172308b4ee2 100644 --- a/rust/chains/tw_aptos/src/nft.rs +++ b/rust/chains/tw_aptos/src/nft.rs @@ -5,7 +5,7 @@ use crate::address::from_account_error; use move_core_types::account_address::AccountAddress; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; use tw_proto::Aptos::Proto::{CancelOfferNftMessage, ClaimNftMessage, NftMessage, OfferNftMessage}; @@ -47,7 +47,8 @@ impl TryFrom> for NftOperation { Ok(NftOperation::Claim(Claim::try_from(msg)?)) }, OneOfnft_transaction_payload::None => { - Err(SigningError(SigningErrorType::Error_invalid_params)) + SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction payload provided") }, } } @@ -106,8 +107,14 @@ impl TryFrom> for Offer { fn try_from(value: CancelOfferNftMessage) -> SigningResult { Ok(Offer { - receiver: AccountAddress::from_str(&value.receiver).map_err(from_account_error)?, - creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + receiver: AccountAddress::from_str(&value.receiver) + .map_err(from_account_error) + .into_tw() + .context("Invalid receiver address")?, + creator: AccountAddress::from_str(&value.creator) + .map_err(from_account_error) + .into_tw() + .context("Invalid creator address")?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, @@ -137,8 +144,14 @@ impl TryFrom> for Claim { fn try_from(value: ClaimNftMessage) -> SigningResult { Ok(Claim { - sender: AccountAddress::from_str(&value.sender).map_err(from_account_error)?, - creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + sender: AccountAddress::from_str(&value.sender) + .map_err(from_account_error) + .into_tw() + .context("Invalid sender address")?, + creator: AccountAddress::from_str(&value.creator) + .map_err(from_account_error) + .into_tw() + .context("Invalid creator address")?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, diff --git a/rust/chains/tw_aptos/src/signer.rs b/rust/chains/tw_aptos/src/signer.rs index 7249e7bec5e..8937e821081 100644 --- a/rust/chains/tw_aptos/src/signer.rs +++ b/rust/chains/tw_aptos/src/signer.rs @@ -5,7 +5,7 @@ use crate::address::Address; use crate::transaction_builder; use std::str::FromStr; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ed25519; use tw_proto::Aptos::Proto; @@ -24,7 +24,9 @@ impl Signer { ) -> SigningResult> { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; - let sender = Address::from_str(&input.sender)?; + let sender = Address::from_str(&input.sender) + .into_tw() + .context("Invalid sender address")?; let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) diff --git a/rust/chains/tw_aptos/src/transaction.rs b/rust/chains/tw_aptos/src/transaction.rs index c4822285566..f4ca4a2c151 100644 --- a/rust/chains/tw_aptos/src/transaction.rs +++ b/rust/chains/tw_aptos/src/transaction.rs @@ -8,7 +8,7 @@ use move_core_types::account_address::AccountAddress; use serde::Serialize; use serde_json::{json, Value}; use std::borrow::Cow; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::encode; use tw_encoding::{bcs, EncodingResult}; use tw_keypair::ed25519::sha512::KeyPair; @@ -128,7 +128,10 @@ impl RawTransaction { } fn msg_to_sign(&self) -> SigningResult { - let serialized = self.serialize()?; + let serialized = self + .serialize() + .into_tw() + .context("Error serializing RawTransaction")?; let mut preimage = tw_hash::sha3::sha3_256(APTOS_SALT); preimage.extend_from_slice(serialized.as_slice()); Ok(preimage) diff --git a/rust/chains/tw_aptos/src/transaction_builder.rs b/rust/chains/tw_aptos/src/transaction_builder.rs index 723121f9910..deae84c8008 100644 --- a/rust/chains/tw_aptos/src/transaction_builder.rs +++ b/rust/chains/tw_aptos/src/transaction_builder.rs @@ -21,7 +21,7 @@ use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use serde_json::Value; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use tw_proto::Aptos::Proto::SigningInput; @@ -49,10 +49,12 @@ impl TransactionBuilder { pub fn build(self) -> SigningResult { let sender = self .sender - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Invalid sender address")?; let sequence_number = self .sequence_number - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Invalid sequence number")?; Ok(RawTransaction::new( sender, sequence_number, @@ -91,22 +93,31 @@ impl TransactionFactory { match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => factory .implicitly_create_user_account_and_transfer( - AccountAddress::from_str(&transfer.to).map_err(from_account_error)?, + AccountAddress::from_str(&transfer.to) + .map_err(from_account_error) + .into_tw() + .context("Invalid destination address")?, transfer.amount, ), OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer .function - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("'TokenTransferMessage::function' is not set")?; factory.coins_transfer( - AccountAddress::from_str(&token_transfer.to).map_err(from_account_error)?, + AccountAddress::from_str(&token_transfer.to) + .map_err(from_account_error) + .into_tw() + .context("Invalid destination address")?, token_transfer.amount, convert_proto_struct_tag_to_type_tag(func)?, ) }, OneOftransaction_payload::create_account(create_account) => { let address = AccountAddress::from_str(&create_account.auth_key) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid 'auth_key' address")?; factory.create_user_account(address) }, OneOftransaction_payload::nft_message(nft_message) => { @@ -115,7 +126,8 @@ impl TransactionFactory { OneOftransaction_payload::register_token(register_token) => { let function = register_token .function - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("'ManagedTokensRegisterMessage::function' is not set")?; Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(function)?)) }, OneOftransaction_payload::liquid_staking_message(msg) => { @@ -124,22 +136,27 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins .function - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("'TokenTransferCoinsMessage::function' is not set")?; factory.implicitly_create_user_and_coins_transfer( AccountAddress::from_str(&token_transfer_coins.to) - .map_err(from_account_error)?, + .map_err(from_account_error) + .into_tw() + .context("Invalid destination address")?, token_transfer_coins.amount, convert_proto_struct_tag_to_type_tag(func)?, ) }, OneOftransaction_payload::None => { let is_blind_sign = !input.any_encoded.is_empty(); - let v = serde_json::from_str::(&input.any_encoded)?; + let v = serde_json::from_str::(&input.any_encoded) + .into_tw() + .context("Error decoding 'SigningInput::any_encoded' as JSON")?; if is_blind_sign { let entry_function = EntryFunction::try_from(v)?; Ok(factory.payload(TransactionPayload::EntryFunction(entry_function))) } else { - Err(SigningError(SigningErrorType::Error_input_parse)) + SigningError::err(SigningErrorType::Error_input_parse) } }, } diff --git a/rust/chains/tw_aptos/src/transaction_payload.rs b/rust/chains/tw_aptos/src/transaction_payload.rs index a14666ad2b5..aa1c74d02da 100644 --- a/rust/chains/tw_aptos/src/transaction_payload.rs +++ b/rust/chains/tw_aptos/src/transaction_payload.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::default::Default; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::{bcs, EncodingError, EncodingResult}; use tw_memory::Data; use tw_proto::Aptos; @@ -36,8 +36,9 @@ impl From for EntryFunctionError { } impl From for SigningError { - fn from(_: EntryFunctionError) -> Self { - SigningError(SigningErrorType::Error_invalid_params) + fn from(e: EntryFunctionError) -> Self { + SigningError::new(SigningErrorType::Error_invalid_params) + .context(format!("Error decoding EntryFunction: {e:?}")) } } @@ -122,7 +123,7 @@ pub fn convert_proto_struct_tag_to_type_tag( "{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name )) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + .tw_err(|_| SigningErrorType::Error_invalid_params) } pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { diff --git a/rust/chains/tw_aptos/tests/signer.rs b/rust/chains/tw_aptos/tests/signer.rs index 5b5268539d4..e69c6894839 100644 --- a/rust/chains/tw_aptos/tests/signer.rs +++ b/rust/chains/tw_aptos/tests/signer.rs @@ -10,7 +10,7 @@ use tw_aptos::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use tw_aptos::nft::{Claim, NftOperation, Offer}; use tw_aptos::signer::Signer; use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_proto::Aptos::Proto; use tw_proto::Aptos::Proto::{SigningInput, SigningOutput}; diff --git a/rust/chains/tw_binance/src/address.rs b/rust/chains/tw_binance/src/address.rs index 3c718a97bd8..45b00e26bb4 100644 --- a/rust/chains/tw_binance/src/address.rs +++ b/rust/chains/tw_binance/src/address.rs @@ -9,7 +9,7 @@ use tw_bech32_address::bech32_prefix::Bech32Prefix; use tw_bech32_address::Bech32Address; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::address::CosmosAddress; use tw_keypair::tw::PublicKey; use tw_memory::Data; diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs index 754cdd854ed..400c37e36ec 100644 --- a/rust/chains/tw_binance/src/compiler.rs +++ b/rust/chains/tw_binance/src/compiler.rs @@ -11,7 +11,7 @@ use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; @@ -89,8 +89,8 @@ impl BinanceCompiler { let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; - let signature_json = serde_json::to_string(&signature_json) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signature_json = + serde_json::to_string(&signature_json).tw_err(|_| SigningErrorType::Error_internal)?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), signature: signature_bytes.into(), diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index f7798bd0f39..cd552881c9d 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -11,7 +11,7 @@ use tw_bech32_address::bech32_prefix::Bech32Prefix; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_binance/src/modules/preimager.rs b/rust/chains/tw_binance/src/modules/preimager.rs index ba881f8beae..45e2dacdf53 100644 --- a/rust/chains/tw_binance/src/modules/preimager.rs +++ b/rust/chains/tw_binance/src/modules/preimager.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::transaction::UnsignedTransaction; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::{sha2, H256}; pub struct JsonTxPreimage { @@ -15,8 +15,8 @@ pub struct JsonPreimager; impl JsonPreimager { pub fn preimage_hash(unsigned: &UnsignedTransaction) -> SigningResult { - let encoded_tx = serde_json::to_string(unsigned) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let encoded_tx = + serde_json::to_string(unsigned).tw_err(|_| SigningErrorType::Error_internal)?; let tx_hash = sha2::sha256(encoded_tx.as_bytes()); let tx_hash = H256::try_from(tx_hash.as_slice()).expect("sha256 must return 32 bytes"); Ok(JsonTxPreimage { diff --git a/rust/chains/tw_binance/src/modules/serializer.rs b/rust/chains/tw_binance/src/modules/serializer.rs index cb1ea5ff4d6..88ec0ad22f3 100644 --- a/rust/chains/tw_binance/src/modules/serializer.rs +++ b/rust/chains/tw_binance/src/modules/serializer.rs @@ -5,7 +5,7 @@ use crate::amino::AminoEncoder; use crate::transaction::SignedTransaction; use std::borrow::Cow; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::public_key::CosmosPublicKey; use tw_memory::Data; use tw_misc::traits::ToBytesVec; @@ -58,6 +58,6 @@ impl BinanceAminoSerializer { sequence: signed.unsigned.sequence, }; // There is no need to use Amino encoding here as the prefix is empty. - serialize(&sign_msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) + serialize(&sign_msg).tw_err(|_| SigningErrorType::Error_internal) } } diff --git a/rust/chains/tw_binance/src/modules/tx_builder.rs b/rust/chains/tw_binance/src/modules/tx_builder.rs index 4b6fb2ebbea..ce1340d63c0 100644 --- a/rust/chains/tw_binance/src/modules/tx_builder.rs +++ b/rust/chains/tw_binance/src/modules/tx_builder.rs @@ -6,7 +6,7 @@ use crate::transaction::message::{BinanceMessageEnum, TWBinanceProto}; use crate::transaction::UnsignedTransaction; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::Binance::Proto; pub struct TxBuilder; @@ -32,7 +32,8 @@ impl TxBuilder { unsigned: &UnsignedTransaction, ) -> SigningResult> { if unsigned.msgs.len() != 1 { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Expected exactly one Transaction Message"); } let msg = unsigned .msgs diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs index a1e60b7da16..e66b4688a2f 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs @@ -5,7 +5,7 @@ use crate::modules::tx_builder::TxBuilder; use crate::modules::wallet_connect::types::SignAminoRequest; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::wallet_connector::WalletConnector; use tw_coin_entry::signing_output_error; use tw_proto::WalletConnect::Proto::{ @@ -32,7 +32,8 @@ impl BinanceWalletConnector { ) -> SigningResult> { match request.method { WCProto::Method::CosmosSignAmino => Self::parse_sign_amino_request(coin, request), - _ => Err(SigningError(SigningErrorType::Error_not_supported)), + _ => SigningError::err(SigningErrorType::Error_not_supported) + .context("Unknown WalletConnect method"), } } @@ -41,7 +42,8 @@ impl BinanceWalletConnector { request: WCProto::ParseRequestInput<'_>, ) -> SigningResult> { let amino_req: SignAminoRequest = serde_json::from_str(&request.payload) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error deserializing WalletConnect signAmino request as JSON")?; // Parse a `SigningInput` from the given `signDoc`. let signing_input = TxBuilder::unsigned_tx_to_proto(&amino_req.sign_doc)?; diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs index b17553e067e..86cb5da5b4b 100644 --- a/rust/chains/tw_binance/src/signer.rs +++ b/rust/chains/tw_binance/src/signer.rs @@ -9,7 +9,7 @@ use crate::modules::tx_builder::TxBuilder; use crate::signature::BinanceSignature; use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; @@ -54,8 +54,8 @@ impl BinanceSigner { }); let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; - let signature_json = serde_json::to_string(&signature_json) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signature_json = + serde_json::to_string(&signature_json).tw_err(|_| SigningErrorType::Error_internal)?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), signature: signature_bytes.into(), diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs index e55e3e4b3e5..902ae019e82 100644 --- a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::as_hex; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs index 5974ba50400..0879bcf5b3e 100644 --- a/rust/chains/tw_binance/src/transaction/message/mod.rs +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize, Serializer}; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto::{self, mod_SigningInput::OneOforder_oneof as BinanceMessageProto}; @@ -138,7 +138,7 @@ impl TWBinanceProto for BinanceMessageEnum { side_chain_delegate::StakeMigrationOrder::from_tw_proto(coin, order) .map(BinanceMessageEnum::StakeMigrationOrder) }, - BinanceMessageProto::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + BinanceMessageProto::None => SigningError::err(SigningErrorType::Error_invalid_params), } } diff --git a/rust/chains/tw_binance/src/transaction/message/send_order.rs b/rust/chains/tw_binance/src/transaction/message/send_order.rs index ef1b5107134..6ffd7d69be2 100644 --- a/rust/chains/tw_binance/src/transaction/message/send_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/send_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs index 690d78def18..4684056cede 100644 --- a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_evm::address::Address as EthereumAddress; use tw_memory::Data; use tw_misc::serde::Typed; @@ -52,7 +52,7 @@ impl TWBinanceProto for SideDelegateOrder { let delegation = msg .delegation .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = SideDelegateOrderValue { delegator_addr, @@ -118,7 +118,7 @@ impl TWBinanceProto for SideRedelegateOrder { let amount = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = SideRedelegateOrderValue { delegator_addr, @@ -182,7 +182,7 @@ impl TWBinanceProto for SideUndelegateOrder { let amount = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = SideUndelegateOrderValue { delegator_addr, @@ -248,7 +248,7 @@ impl TWBinanceProto for StakeMigrationOrder { let amount = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = StakeMigrationOrderValue { amount: Token::from_tw_proto(amount), diff --git a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs index 2647d6553cb..9e2e4e21e8c 100644 --- a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/token_order.rs b/rust/chains/tw_binance/src/transaction/message/token_order.rs index ecff5236773..ec88ba88e26 100644 --- a/rust/chains/tw_binance/src/transaction/message/token_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/token_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/trade_order.rs b/rust/chains/tw_binance/src/transaction/message/trade_order.rs index 5aced178ea6..0add4dcd87b 100644 --- a/rust/chains/tw_binance/src/transaction/message/trade_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/trade_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; @@ -61,7 +61,7 @@ impl TWBinanceProto for NewTradeOrder { fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { let order_type = OrderType::from_repr(msg.ordertype) - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let sender = BinanceAddress::from_key_hash_with_coin(coin, msg.sender.to_vec())?; Ok(NewTradeOrder { diff --git a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs index ae8aabff1ca..30dfef73f56 100644 --- a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_evm::address::Address as EthereumAddress; use tw_hash::H160; use tw_memory::Data; @@ -41,14 +41,14 @@ impl TWBinanceProto for TransferOutOrder { fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; - let to_bytes = H160::try_from(msg.to.as_ref()) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + let to_bytes = + H160::try_from(msg.to.as_ref()).tw_err(|_| SigningErrorType::Error_invalid_address)?; let to = EthereumAddress::from_bytes(to_bytes); let amount_proto = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; Ok(TransferOutOrder { from, diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index 1f8650a70cd..67a15bc2d1e 100644 --- a/rust/chains/tw_cosmos/src/entry.rs +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_ethereum/src/entry.rs b/rust/chains/tw_ethereum/src/entry.rs index 86b351aad25..13f48c92701 100644 --- a/rust/chains/tw_ethereum/src/entry.rs +++ b/rust/chains/tw_ethereum/src/entry.rs @@ -6,7 +6,8 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; @@ -15,7 +16,6 @@ use tw_evm::address::Address; use tw_evm::evm_context::StandardEvmContext; use tw_evm::evm_entry::EvmEntry; use tw_evm::modules::compiler::Compiler; -use tw_evm::modules::json_signer::EthJsonSigner; use tw_evm::modules::message_signer::EthMessageSigner; use tw_evm::modules::signer::Signer; use tw_keypair::tw::PublicKey; @@ -32,7 +32,7 @@ impl CoinEntry for EthereumEntry { type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; // Optional modules: - type JsonSigner = EthJsonSigner; + type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; type WalletConnector = NoWalletConnector; @@ -96,11 +96,6 @@ impl CoinEntry for EthereumEntry { Compiler::::compile(input, signatures, public_keys) } - #[inline] - fn json_signer(&self) -> Option { - Some(EthJsonSigner::default()) - } - #[inline] fn message_signer(&self) -> Option { Some(EthMessageSigner) diff --git a/rust/chains/tw_ethereum/tests/compiler.rs b/rust/chains/tw_ethereum/tests/compiler.rs index bdf14b3e2a9..7d88e7279e2 100644 --- a/rust/chains/tw_ethereum/tests/compiler.rs +++ b/rust/chains/tw_ethereum/tests/compiler.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex; use tw_ethereum::entry::EthereumEntry; diff --git a/rust/chains/tw_greenfield/src/address.rs b/rust/chains/tw_greenfield/src/address.rs index d0d4d794448..94b9565b17a 100644 --- a/rust/chains/tw_greenfield/src/address.rs +++ b/rust/chains/tw_greenfield/src/address.rs @@ -6,7 +6,7 @@ use serde::Serialize; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::address::CosmosAddress; use tw_evm::address::Address as EthereumAddress; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/chains/tw_greenfield/src/compiler.rs b/rust/chains/tw_greenfield/src/compiler.rs index a37ac39a7f2..e9db4725199 100644 --- a/rust/chains/tw_greenfield/src/compiler.rs +++ b/rust/chains/tw_greenfield/src/compiler.rs @@ -11,7 +11,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_cosmos_sdk::modules::broadcast_msg::{BroadcastMode, BroadcastMsg}; use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; @@ -72,8 +72,12 @@ impl GreenfieldCompiler { } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let public_key_params = None; - let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key, public_key_params)?; - let signature = GreenfieldSignature::try_from(raw_signature.as_slice())?; + let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key, public_key_params) + .into_tw() + .context("Invalid provided public key")?; + let signature = GreenfieldSignature::try_from(raw_signature.as_slice()) + .into_tw() + .context("Invalid provided signature")?; let signature_bytes = signature.to_vec(); // Set the public key. It will be used to construct a signer info. @@ -91,7 +95,8 @@ impl GreenfieldCompiler { signature_bytes.clone(), ); let signature_json = serde_json::to_string(&[signature_json]) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signatures as JSON")?; Ok(Proto::SigningOutput { signature: Cow::from(signature_bytes), diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index 52ea07203f4..01ee90e50ae 100644 --- a/rust/chains/tw_greenfield/src/entry.rs +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs index 7b9fcb34f05..c5ead1f19de 100644 --- a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs +++ b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs @@ -7,10 +7,10 @@ use crate::eip712_types::{ }; use crate::transaction::GreenfieldUnsignedTransaction; use std::collections::BTreeMap; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_evm::message::eip712::eip712_message::Eip712Message; use tw_evm::message::eip712::message_types::MessageTypesBuilder; -use tw_evm::message::EthMessage; +use tw_evm::message::{to_signing, EthMessage}; use tw_hash::H256; use tw_number::U256; @@ -65,15 +65,17 @@ impl Eip712Signer { let msg_to_sign = Eip712Message { types: types_builder.build(), domain: serde_json::to_value(domain) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?, + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing EIP712Domain as JSON")?, primary_type: Eip712Transaction::TYPE_NAME.to_string(), message: serde_json::to_value(tx_to_sign) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?, + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing EIP712 message payload as JSON")?, }; - let tx_hash = msg_to_sign.hash()?; - let eip712_tx = serde_json::to_string(&msg_to_sign) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let tx_hash = msg_to_sign.hash().map_err(to_signing)?; + let eip712_tx = + serde_json::to_string(&msg_to_sign).tw_err(|_| SigningErrorType::Error_internal)?; Ok(Eip712TxPreimage { eip712_tx, tx_hash }) } diff --git a/rust/chains/tw_greenfield/src/modules/tx_builder.rs b/rust/chains/tw_greenfield/src/modules/tx_builder.rs index 2e0951c8cce..6a12bb2aa6e 100644 --- a/rust/chains/tw_greenfield/src/modules/tx_builder.rs +++ b/rust/chains/tw_greenfield/src/modules/tx_builder.rs @@ -11,7 +11,7 @@ use crate::transaction::{ }; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::public_key::CosmosPublicKey; use tw_cosmos_sdk::transaction::{Coin, SignerInfo}; use tw_misc::traits::OptionalEmpty; @@ -36,11 +36,13 @@ impl TxBuilder { let fee = input .fee .as_ref() - .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + .or_tw_err(SigningErrorType::Error_wrong_fee) + .context("No 'fee' specified")?; let fee = Self::fee_from_proto(fee, &signer)?; let eth_chain_id = U256::from_str(&input.eth_chain_id) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid ETH chain ID")?; Ok(GreenfieldUnsignedTransaction { signer, @@ -89,7 +91,9 @@ impl TxBuilder { } fn coin_from_proto(input: &Proto::Amount<'_>) -> SigningResult { - let amount = U256::from_str(&input.amount)?; + let amount = U256::from_str(&input.amount) + .into_tw() + .context("Invalid amount: expected uint256 decimal-string")?; Ok(Coin { amount, denom: input.denom.to_string(), @@ -98,7 +102,8 @@ impl TxBuilder { fn tx_body_from_proto(input: &Proto::SigningInput<'_>) -> SigningResult { if input.messages.is_empty() { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction messages provided"); } let messages = input @@ -122,7 +127,8 @@ impl TxBuilder { MessageEnum::bridge_transfer_out(ref transfer_out) => { Self::bridge_transfer_out_from_proto(transfer_out) }, - MessageEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + MessageEnum::None => SigningError::err(SigningErrorType::Error_invalid_params) + .context("No message type provided"), } } @@ -139,8 +145,12 @@ impl TxBuilder { .collect::>()?; let msg = SendMessage { custom_type_prefix: send.type_prefix.to_string().empty_or_some(), - from_address: GreenfieldAddress::from_str(&send.from_address)?, - to_address: GreenfieldAddress::from_str(&send.to_address)?, + from_address: GreenfieldAddress::from_str(&send.from_address) + .into_tw() + .context("Invalid sender address")?, + to_address: GreenfieldAddress::from_str(&send.to_address) + .into_tw() + .context("Invalid recipient address")?, amount: amounts, }; Ok(Box::new(GreenfieldSendMessage(msg))) @@ -154,13 +164,18 @@ impl TxBuilder { let amount = transfer_out .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No transfer amount specified")?; let msg = GreenfieldTransferOut { custom_type_prefix: transfer_out.type_prefix.to_string().empty_or_some(), amount: Self::coin_from_proto(amount)?, - from: GreenfieldAddress::from_str(&transfer_out.from_address)?, - to: GreenfieldAddress::from_str(&transfer_out.to_address)?, + from: GreenfieldAddress::from_str(&transfer_out.from_address) + .into_tw() + .context("Invalid sender address")?, + to: GreenfieldAddress::from_str(&transfer_out.to_address) + .into_tw() + .context("Invalid recipient address")?, }; Ok(Box::new(msg)) } diff --git a/rust/chains/tw_greenfield/src/signer.rs b/rust/chains/tw_greenfield/src/signer.rs index 6b0d2c88e28..afde1ed2747 100644 --- a/rust/chains/tw_greenfield/src/signer.rs +++ b/rust/chains/tw_greenfield/src/signer.rs @@ -7,7 +7,7 @@ use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; use crate::modules::tx_builder::TxBuilder; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ecdsa::secp256k1; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; diff --git a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs index 8d9ebc62ccf..7733629ba9d 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs @@ -5,7 +5,7 @@ use crate::address::GreenfieldAddress; use crate::transaction::message::type_msg_amount::TypeMsgAmount; use crate::transaction::message::GreenfieldMessage; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::proto::cosmos as CosmosProto; use tw_cosmos_sdk::transaction::message::cosmos_bank_message::SendMessage; use tw_cosmos_sdk::transaction::message::{ diff --git a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs index f626db7d5a7..00a01116db5 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs @@ -6,7 +6,7 @@ use crate::address::GreenfieldAddress; use crate::transaction::message::type_msg_amount::TypeMsgAmount; use crate::transaction::message::GreenfieldMessage; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::modules::serializer::protobuf_serializer::build_coin; use tw_cosmos_sdk::proto::greenfield as GreenfieldProto; use tw_cosmos_sdk::transaction::message::{ diff --git a/rust/chains/tw_internet_computer/src/address.rs b/rust/chains/tw_internet_computer/src/address.rs index 80821df6f70..a30ab2ef984 100644 --- a/rust/chains/tw_internet_computer/src/address.rs +++ b/rust/chains/tw_internet_computer/src/address.rs @@ -2,10 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::{ - coin_entry::CoinAddress, - error::{AddressError, AddressResult}, -}; +use tw_coin_entry::{coin_entry::CoinAddress, error::prelude::*}; use tw_encoding::hex; use tw_hash::{crc32::crc32, sha2::sha224, H256}; use tw_keypair::ecdsa::secp256k1::PublicKey; diff --git a/rust/chains/tw_internet_computer/src/context.rs b/rust/chains/tw_internet_computer/src/context.rs index f429d4bc5db..41cee32fb86 100644 --- a/rust/chains/tw_internet_computer/src/context.rs +++ b/rust/chains/tw_internet_computer/src/context.rs @@ -30,7 +30,7 @@ mod test { use std::marker::PhantomData; - use tw_coin_entry::error::AddressResult; + use tw_coin_entry::error::prelude::*; use super::*; diff --git a/rust/chains/tw_internet_computer/src/entry.rs b/rust/chains/tw_internet_computer/src/entry.rs index 446ed2367fd..a11d07ad9cc 100644 --- a/rust/chains/tw_internet_computer/src/entry.rs +++ b/rust/chains/tw_internet_computer/src/entry.rs @@ -8,7 +8,7 @@ use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::{ coin_context::CoinContext, coin_entry::CoinEntry, - error::{AddressError, AddressResult, SigningError}, + error::prelude::*, modules::{ json_signer::NoJsonSigner, message_signer::NoMessageSigner, plan_builder::NoPlanBuilder, wallet_connector::NoWalletConnector, @@ -66,7 +66,7 @@ impl CoinEntry for InternetComputerEntry { public_key: tw_keypair::tw::PublicKey, _derivation: tw_coin_entry::derivation::Derivation, _prefix: Option, - ) -> tw_coin_entry::error::AddressResult { + ) -> AddressResult { let secp256k1_public_key = public_key .to_secp256k1() .ok_or(AddressError::PublicKeyTypeMismatch)?; @@ -89,7 +89,7 @@ impl CoinEntry for InternetComputerEntry { ) -> Self::PreSigningOutput { signing_output_error!( CompilerProto::PreSigningOutput, - SigningError(CommonError::Error_not_supported) + SigningError::new(CommonError::Error_not_supported) ) } @@ -102,7 +102,7 @@ impl CoinEntry for InternetComputerEntry { ) -> Self::SigningOutput { signing_output_error!( Proto::SigningOutput, - SigningError(CommonError::Error_not_supported) + SigningError::new(CommonError::Error_not_supported) ) } } diff --git a/rust/chains/tw_internet_computer/src/signer.rs b/rust/chains/tw_internet_computer/src/signer.rs index 311fd587f44..ee4a39a76de 100644 --- a/rust/chains/tw_internet_computer/src/signer.rs +++ b/rust/chains/tw_internet_computer/src/signer.rs @@ -4,10 +4,7 @@ use std::marker::PhantomData; -use tw_coin_entry::{ - error::{SigningError, SigningResult}, - signing_output_error, -}; +use tw_coin_entry::{error::prelude::*, signing_output_error}; use tw_keypair::ecdsa::secp256k1; use tw_proto::{Common::Proto::SigningError as CommonError, InternetComputer::Proto}; @@ -21,20 +18,20 @@ impl From for SigningError { fn from(error: transactions::SignTransactionError) -> Self { match error { transactions::SignTransactionError::InvalidArguments => { - SigningError(CommonError::Error_invalid_params) + SigningError::new(CommonError::Error_invalid_params) }, transactions::SignTransactionError::Identity(identity_error) => match identity_error { - identity::SigningError::Failed(_) => SigningError(CommonError::Error_signing), + identity::SigningError::Failed(_) => SigningError::new(CommonError::Error_signing), }, transactions::SignTransactionError::InvalidEnvelopePair | transactions::SignTransactionError::EncodingArgsFailed => { - SigningError(CommonError::Error_internal) + SigningError::new(CommonError::Error_internal) }, transactions::SignTransactionError::InvalidToAccountIdentifier => { - SigningError(CommonError::Error_invalid_address) + SigningError::new(CommonError::Error_invalid_address) }, transactions::SignTransactionError::InvalidAmount => { - SigningError(CommonError::Error_invalid_requested_token_amount) + SigningError::new(CommonError::Error_invalid_requested_token_amount) }, } } @@ -57,16 +54,15 @@ impl Signer { let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; let Some(ref transaction) = input.transaction else { - return Err(SigningError(CommonError::Error_invalid_params)); + return SigningError::err(CommonError::Error_invalid_params); }; let canister_id = Context::get_canister_id(); let signed_transaction = - sign_transaction(private_key, canister_id, &transaction.transaction_oneof) - .map_err(SigningError::from)?; + sign_transaction(private_key, canister_id, &transaction.transaction_oneof)?; let cbor_encoded_signed_transaction = tw_encoding::cbor::encode(&signed_transaction) - .map_err(|_| SigningError(CommonError::Error_internal))?; + .tw_err(|_| CommonError::Error_internal)?; Ok(Proto::SigningOutput { signed_transaction: cbor_encoded_signed_transaction.into(), diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs index 9586259983c..86a9e715b73 100644 --- a/rust/chains/tw_native_evmos/src/entry.rs +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index e65ab7fa0a8..3a16d84836c 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_ronin/src/address.rs b/rust/chains/tw_ronin/src/address.rs index ad4a4c076ea..73bdaf91624 100644 --- a/rust/chains/tw_ronin/src/address.rs +++ b/rust/chains/tw_ronin/src/address.rs @@ -5,7 +5,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_evm::address::{Address as EthAddress, EvmAddress}; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; diff --git a/rust/chains/tw_ronin/src/entry.rs b/rust/chains/tw_ronin/src/entry.rs index 2bf767d2af6..1c3a9ab674d 100644 --- a/rust/chains/tw_ronin/src/entry.rs +++ b/rust/chains/tw_ronin/src/entry.rs @@ -8,14 +8,14 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_evm::evm_entry::EvmEntry; use tw_evm::modules::compiler::Compiler; -use tw_evm::modules::json_signer::EthJsonSigner; use tw_evm::modules::message_signer::EthMessageSigner; use tw_evm::modules::signer::Signer; use tw_keypair::tw::PublicKey; @@ -32,7 +32,7 @@ impl CoinEntry for RoninEntry { type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; // Optional modules: - type JsonSigner = EthJsonSigner; + type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; type WalletConnector = NoWalletConnector; @@ -95,11 +95,6 @@ impl CoinEntry for RoninEntry { Compiler::::compile(input, signatures, public_keys) } - #[inline] - fn json_signer(&self) -> Option { - Some(EthJsonSigner::default()) - } - #[inline] fn message_signer(&self) -> Option { Some(EthMessageSigner) diff --git a/rust/chains/tw_ronin/tests/compiler.rs b/rust/chains/tw_ronin/tests/compiler.rs index f755ed12431..18d55a3fd92 100644 --- a/rust/chains/tw_ronin/tests/compiler.rs +++ b/rust/chains/tw_ronin/tests/compiler.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/chains/tw_ronin/tests/rlp.rs b/rust/chains/tw_ronin/tests/rlp.rs index 1bfce0fa625..d3d9548fd7b 100644 --- a/rust/chains/tw_ronin/tests/rlp.rs +++ b/rust/chains/tw_ronin/tests/rlp.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use std::borrow::Cow; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::ToHex; use tw_evm::evm_entry::EvmEntryExt; use tw_proto::EthereumRlp::Proto as RlpProto; diff --git a/rust/chains/tw_ronin/tests/signer.rs b/rust/chains/tw_ronin/tests/signer.rs index 16e7e747cbd..309592e9278 100644 --- a/rust/chains/tw_ronin/tests/signer.rs +++ b/rust/chains/tw_ronin/tests/signer.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_number::U256; diff --git a/rust/chains/tw_solana/src/address.rs b/rust/chains/tw_solana/src/address.rs index f15718964eb..9832509cdfa 100644 --- a/rust/chains/tw_solana/src/address.rs +++ b/rust/chains/tw_solana/src/address.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58; use tw_hash::{as_byte_sequence, sha2, H256}; use tw_keypair::{ed25519, tw}; diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs index ca50e214776..483ddac98cb 100644 --- a/rust/chains/tw_solana/src/compiler.rs +++ b/rust/chains/tw_solana/src/compiler.rs @@ -11,7 +11,7 @@ use std::borrow::Cow; use std::collections::HashMap; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::{base58, base64}; use tw_keypair::ed25519; @@ -73,7 +73,8 @@ impl SolanaCompiler { }; if signatures.len() != public_keys.len() { - return Err(SigningError(SigningErrorType::Error_signatures_count)); + return SigningError::err(SigningErrorType::Error_signatures_count) + .context("Expected the same number of signatures and public keys"); } let builder = MessageBuilder::new(input); @@ -89,7 +90,8 @@ impl SolanaCompiler { if !pubkey.verify(signature.clone(), data_to_sign.clone()) && !signature.to_bytes().is_zero() { - return Err(SigningError(SigningErrorType::Error_signing)); + return SigningError::err(SigningErrorType::Error_signing) + .context("Error verifying the given signature"); } key_signs.insert(SolanaAddress::with_public_key_ed25519(&pubkey), signature); @@ -98,7 +100,8 @@ impl SolanaCompiler { let signed_tx = TxSigner::compile_versioned(unsigned_msg, key_signs)?; let signed_encoded = bincode::serialize(&signed_tx) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signed transaction")?; let signed_encoded = encode(&signed_encoded); let unsigned_encoded = encode(&data_to_sign); diff --git a/rust/chains/tw_solana/src/entry.rs b/rust/chains/tw_solana/src/entry.rs index 62356a87bf6..cc30d20c6fa 100644 --- a/rust/chains/tw_solana/src/entry.rs +++ b/rust/chains/tw_solana/src/entry.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_solana/src/modules/compiled_instructions.rs b/rust/chains/tw_solana/src/modules/compiled_instructions.rs index 92d16320a16..4e0e02a0c9c 100644 --- a/rust/chains/tw_solana/src/modules/compiled_instructions.rs +++ b/rust/chains/tw_solana/src/modules/compiled_instructions.rs @@ -5,7 +5,7 @@ use crate::address::SolanaAddress; use crate::instruction::Instruction; use crate::transaction::CompiledInstruction; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; pub fn compile_instructions( ixs: &[Instruction], @@ -18,7 +18,7 @@ fn position(keys: &[SolanaAddress], key: &SolanaAddress) -> SigningResult { keys.iter() .position(|k| k == key) .map(|k| k as u8) - .ok_or(SigningError(SigningErrorType::Error_internal)) + .or_tw_err(SigningErrorType::Error_internal) } /// https://github.com/solana-labs/solana/blob/4b65cc8eef6ef79cb9b9cbc534a99b4900e58cf7/sdk/program/src/message/legacy.rs#L72-L84 @@ -30,10 +30,12 @@ pub(crate) fn compile_instruction( .accounts .iter() .map(|account_meta| position(keys, &account_meta.pubkey)) - .collect::>>()?; + .collect::>>() + .context("Cannot build account metas")?; Ok(CompiledInstruction { - program_id_index: position(keys, &ix.program_id)?, + program_id_index: position(keys, &ix.program_id) + .context("Program ID account is not provided")?, data: ix.data.clone(), accounts, }) diff --git a/rust/chains/tw_solana/src/modules/compiled_keys.rs b/rust/chains/tw_solana/src/modules/compiled_keys.rs index 3272f17b64b..e138c1d9b29 100644 --- a/rust/chains/tw_solana/src/modules/compiled_keys.rs +++ b/rust/chains/tw_solana/src/modules/compiled_keys.rs @@ -9,7 +9,7 @@ use crate::instruction::Instruction; use crate::transaction::MessageHeader; use std::collections::hash_map::Entry; use std::collections::HashMap; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] struct CompiledKeyMeta { @@ -68,7 +68,7 @@ impl CompiledKeys { pub fn try_into_message_components(self) -> SigningResult<(MessageHeader, Vec)> { let try_into_u8 = |num: usize| -> SigningResult { - u8::try_from(num).map_err(|_| SigningError(SigningErrorType::Error_tx_too_big)) + u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big) }; let Self { @@ -103,9 +103,12 @@ impl CompiledKeys { .saturating_add(readonly_signer_keys.len()); let header = MessageHeader { - num_required_signatures: try_into_u8(signers_len)?, - num_readonly_signed_accounts: try_into_u8(readonly_signer_keys.len())?, - num_readonly_unsigned_accounts: try_into_u8(readonly_non_signer_keys.len())?, + num_required_signatures: try_into_u8(signers_len) + .context("Too many signatures required")?, + num_readonly_signed_accounts: try_into_u8(readonly_signer_keys.len()) + .context("Too many accounts in the transaction")?, + num_readonly_unsigned_accounts: try_into_u8(readonly_non_signer_keys.len()) + .context("Too many accounts in the transaction")?, }; let static_account_keys: Vec<_> = std::iter::empty() diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs index a0dfd9139fb..ad02414ec72 100644 --- a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs +++ b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs @@ -9,7 +9,6 @@ use crate::instruction::{AccountMeta, Instruction}; use crate::modules::instruction_builder::system_instruction::SystemInstructionBuilder; use crate::program::stake_program::StakeProgram; use serde::{Deserialize, Serialize}; -use tw_coin_entry::error::SigningResult; type UnixTimestamp = i64; type Epoch = u64; @@ -373,7 +372,7 @@ impl StakeInstructionBuilder { } /// The function represents "stake delegation" operation that consists of several small instructions. - pub fn deposit_stake(args: DepositStakeArgs) -> SigningResult> { + pub fn deposit_stake(args: DepositStakeArgs) -> Vec { let stake_addr = args.stake_account.unwrap_or_else(|| { // no stake address specified, generate a new unique StakeProgram::address_from_recent_blockhash(&args.sender, &args.recent_blockhash) @@ -386,7 +385,7 @@ impl StakeInstructionBuilder { }; let lockup = Lockup::default(); - Ok(vec![ + vec![ SystemInstructionBuilder::create_account_with_seed( args.sender, stake_addr, @@ -397,6 +396,6 @@ impl StakeInstructionBuilder { ), StakeInstructionBuilder::stake_initialize(stake_addr, authorized, lockup), StakeInstructionBuilder::delegate(stake_addr, args.validator, args.sender), - ]) + ] } } diff --git a/rust/chains/tw_solana/src/modules/message_builder.rs b/rust/chains/tw_solana/src/modules/message_builder.rs index 59cc70fc739..0ce11d8b67a 100644 --- a/rust/chains/tw_solana/src/modules/message_builder.rs +++ b/rust/chains/tw_solana/src/modules/message_builder.rs @@ -19,7 +19,7 @@ use crate::transaction::versioned::VersionedMessage; use crate::transaction::{legacy, v0, CompiledInstruction, MessageHeader, Signature}; use std::borrow::Cow; use std::str::FromStr; -use tw_coin_entry::error::{AddressResult, SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_keypair::ed25519; use tw_keypair::traits::KeyPairTrait; use tw_proto::Solana::Proto; @@ -41,7 +41,9 @@ impl<'a> MessageBuilder<'a> { let mut signing_keys = Vec::default(); if !self.input.fee_payer_private_key.is_empty() { let fee_payer_private_key = - ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref())?; + ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref()) + .into_tw() + .context("Invalid fee payer private key")?; signing_keys.push(fee_payer_private_key); } @@ -50,7 +52,9 @@ impl<'a> MessageBuilder<'a> { // Consider matching other transaction types if they may contain other private keys. if let ProtoTransactionType::create_nonce_account(ref nonce) = self.input.transaction_type { let nonce_private_key = - ed25519::sha512::KeyPair::try_from(nonce.nonce_account_private_key.as_ref())?; + ed25519::sha512::KeyPair::try_from(nonce.nonce_account_private_key.as_ref()) + .into_tw() + .context("Invalid nonce account private key")?; signing_keys.push(nonce_private_key); } @@ -135,7 +139,8 @@ impl<'a> MessageBuilder<'a> { ProtoTransactionType::advance_nonce_account(ref advance_nonce) => { self.advance_nonce_from_proto(advance_nonce) }, - ProtoTransactionType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + ProtoTransactionType::None => SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction type specified"), } } @@ -144,7 +149,10 @@ impl<'a> MessageBuilder<'a> { transfer: &Proto::Transfer<'_>, ) -> SigningResult> { let from = self.signer_address()?; - let to = SolanaAddress::from_str(&transfer.recipient)?; + let to = SolanaAddress::from_str(&transfer.recipient) + .into_tw() + .context("Invalid recipient address")?; + let references = Self::parse_references(&transfer.references)?; let transfer_ix = SystemInstructionBuilder::transfer(from, to, transfer.value) @@ -165,12 +173,16 @@ impl<'a> MessageBuilder<'a> { delegate: &Proto::DelegateStake, ) -> SigningResult> { let sender = self.signer_address()?; - let validator = SolanaAddress::from_str(delegate.validator_pubkey.as_ref())?; + let validator = SolanaAddress::from_str(delegate.validator_pubkey.as_ref()) + .into_tw() + .context("Invalid validator address")?; let stake_account = if delegate.stake_account.is_empty() { None } else { - let stake_account = SolanaAddress::from_str(&delegate.stake_account)?; + let stake_account = SolanaAddress::from_str(&delegate.stake_account) + .into_tw() + .context("Invalid stake account")?; Some(stake_account) }; @@ -181,7 +193,7 @@ impl<'a> MessageBuilder<'a> { recent_blockhash: self.recent_blockhash()?, lamports: delegate.value, space: DEFAULT_SPACE, - })?; + }); let mut builder = InstructionBuilder::default(); builder @@ -197,7 +209,10 @@ impl<'a> MessageBuilder<'a> { deactivate: &Proto::DeactivateStake, ) -> SigningResult> { let sender = self.signer_address()?; - let stake_account = SolanaAddress::from_str(&deactivate.stake_account)?; + let stake_account = SolanaAddress::from_str(&deactivate.stake_account) + .into_tw() + .context("Invalid stake account")?; + let deactivate_ix = StakeInstructionBuilder::deactivate(stake_account, sender); let mut builder = InstructionBuilder::default(); @@ -221,7 +236,8 @@ impl<'a> MessageBuilder<'a> { let stake_account = SolanaAddress::from_str(stake_account.as_ref())?; Ok(StakeInstructionBuilder::deactivate(stake_account, sender)) }) - .collect::>>()?; + .collect::>>() + .context("Invalid stake account(s)")?; let mut builder = InstructionBuilder::default(); builder @@ -237,7 +253,10 @@ impl<'a> MessageBuilder<'a> { withdraw: &Proto::WithdrawStake, ) -> SigningResult> { let sender = self.signer_address()?; - let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref())?; + let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref()) + .into_tw() + .context("Invalid stake account")?; + let custodian_account = None; let withdraw_ix = StakeInstructionBuilder::withdraw( @@ -267,7 +286,10 @@ impl<'a> MessageBuilder<'a> { .stake_accounts .iter() .map(|withdraw| { - let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref())?; + let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref()) + .into_tw() + .context("Invalid stake account")?; + let custodian_account = None; Ok(StakeInstructionBuilder::withdraw( stake_account, @@ -293,10 +315,18 @@ impl<'a> MessageBuilder<'a> { create_token_acc: &Proto::CreateTokenAccount, ) -> SigningResult> { let funding_account = self.signer_address()?; - let other_main_address = SolanaAddress::from_str(create_token_acc.main_address.as_ref())?; + let other_main_address = SolanaAddress::from_str(create_token_acc.main_address.as_ref()) + .into_tw() + .context("Invalid main address")?; + let token_mint_address = - SolanaAddress::from_str(create_token_acc.token_mint_address.as_ref())?; - let token_address = SolanaAddress::from_str(create_token_acc.token_address.as_ref())?; + SolanaAddress::from_str(create_token_acc.token_mint_address.as_ref()) + .into_tw() + .context("Invalid token mint address")?; + + let token_address = SolanaAddress::from_str(create_token_acc.token_address.as_ref()) + .into_tw() + .context("Invalid token address to be created")?; let instruction = TokenInstructionBuilder::create_account( funding_account, @@ -319,16 +349,26 @@ impl<'a> MessageBuilder<'a> { ) -> SigningResult> { let signer = self.signer_address()?; let sender_token_address = - SolanaAddress::from_str(token_transfer.sender_token_address.as_ref())?; + SolanaAddress::from_str(token_transfer.sender_token_address.as_ref()) + .into_tw() + .context("Invalid sender token address")?; + let token_mint_address = - SolanaAddress::from_str(token_transfer.token_mint_address.as_ref())?; + SolanaAddress::from_str(token_transfer.token_mint_address.as_ref()) + .into_tw() + .context("Invalid token mint address")?; + let recipient_token_address = - SolanaAddress::from_str(token_transfer.recipient_token_address.as_ref())?; + SolanaAddress::from_str(token_transfer.recipient_token_address.as_ref()) + .into_tw() + .context("Invalid recipient token address")?; let decimals = token_transfer .decimals .try_into() - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid token decimals. Expected lower than 256")?; + let references = Self::parse_references(&token_transfer.references)?; let transfer_instruction = TokenInstructionBuilder::transfer_checked( @@ -357,20 +397,34 @@ impl<'a> MessageBuilder<'a> { ) -> SigningResult> { let signer = self.signer_address()?; let fee_payer = self.fee_payer()?; + let sender_token_address = - SolanaAddress::from_str(create_and_transfer.sender_token_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.sender_token_address.as_ref()) + .into_tw() + .context("Invalid sender token address")?; + let token_mint_address = - SolanaAddress::from_str(create_and_transfer.token_mint_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.token_mint_address.as_ref()) + .into_tw() + .context("Invalid token mint address")?; + let recipient_main_address = - SolanaAddress::from_str(create_and_transfer.recipient_main_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.recipient_main_address.as_ref()) + .into_tw() + .context("Invalid recipient main address")?; + let recipient_token_address = - SolanaAddress::from_str(create_and_transfer.recipient_token_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.recipient_token_address.as_ref()) + .into_tw() + .context("Invalid recipient token address")?; + let references = Self::parse_references(&create_and_transfer.references)?; let decimals = create_and_transfer .decimals .try_into() - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid token decimals. Expected lower than 256")?; let create_account_instruction = TokenInstructionBuilder::create_account( // Can be different from the actual signer. @@ -409,12 +463,15 @@ impl<'a> MessageBuilder<'a> { let prev_nonce_account = self.nonce_account()?; let new_nonce_account = if create_nonce.nonce_account.is_empty() { - let nonce_key = ed25519::sha512::KeyPair::try_from( - create_nonce.nonce_account_private_key.as_ref(), - )?; + let nonce_key = + ed25519::sha512::KeyPair::try_from(create_nonce.nonce_account_private_key.as_ref()) + .into_tw() + .context("Invalid nonce account private key")?; SolanaAddress::with_public_key_ed25519(nonce_key.public()) } else { - SolanaAddress::from_str(create_nonce.nonce_account.as_ref())? + SolanaAddress::from_str(create_nonce.nonce_account.as_ref()) + .into_tw() + .context("Invalid nonce account")? }; let mut builder = InstructionBuilder::default(); @@ -436,8 +493,13 @@ impl<'a> MessageBuilder<'a> { withdraw_nonce: &Proto::WithdrawNonceAccount, ) -> SigningResult> { let signer = self.signer_address()?; - let withdraw_from_nonce = SolanaAddress::from_str(withdraw_nonce.nonce_account.as_ref())?; - let recipient = SolanaAddress::from_str(withdraw_nonce.recipient.as_ref())?; + let withdraw_from_nonce = SolanaAddress::from_str(withdraw_nonce.nonce_account.as_ref()) + .into_tw() + .context("Invalid nonce account")?; + + let recipient = SolanaAddress::from_str(withdraw_nonce.recipient.as_ref()) + .into_tw() + .context("Invalid recipient")?; let mut builder = InstructionBuilder::default(); builder @@ -458,7 +520,9 @@ impl<'a> MessageBuilder<'a> { advance_nonce: &Proto::AdvanceNonceAccount, ) -> SigningResult> { let signer = self.signer_address()?; - let nonce_account = SolanaAddress::from_str(advance_nonce.nonce_account.as_ref())?; + let nonce_account = SolanaAddress::from_str(advance_nonce.nonce_account.as_ref()) + .into_tw() + .context("Invalid nonce account")?; let mut builder = InstructionBuilder::default(); builder @@ -475,12 +539,15 @@ impl<'a> MessageBuilder<'a> { SolanaAddress::from_str(&self.input.nonce_account) .map(Some) .map_err(SigningError::from) + .context("Invalid nonce account") } } fn signer_address(&self) -> SigningResult { if self.input.private_key.is_empty() { - SolanaAddress::from_str(&self.input.sender).map_err(SigningError::from) + SolanaAddress::from_str(&self.input.sender) + .map_err(SigningError::from) + .context("Sender address is either not set or invalid") } else { Ok(SolanaAddress::with_public_key_ed25519( self.signer_key()?.public(), @@ -493,25 +560,31 @@ impl<'a> MessageBuilder<'a> { pub fn fee_payer(&self) -> SigningResult { if !self.input.fee_payer_private_key.is_empty() { let fee_payer_key = - ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref())?; + ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref()) + .into_tw() + .context("Invalid fee payer private key")?; return Ok(SolanaAddress::with_public_key_ed25519( fee_payer_key.public(), )); } if !self.input.fee_payer.is_empty() { return SolanaAddress::from_str(self.input.fee_payer.as_ref()) - .map_err(SigningError::from); + .map_err(SigningError::from) + .context("Invalid fee payer address"); } self.signer_address() } fn recent_blockhash(&self) -> SigningResult { - Ok(Blockhash::from_str(&self.input.recent_blockhash)?) + Blockhash::from_str(&self.input.recent_blockhash) + .map_err(SigningError::from) + .context("Invalid recent blockhash") } fn signer_key(&self) -> SigningResult { ed25519::sha512::KeyPair::try_from(self.input.private_key.as_ref()) .map_err(SigningError::from) + .context("Invalid signer key") } fn priority_fee_price(&self) -> Option { @@ -531,7 +604,8 @@ impl<'a> MessageBuilder<'a> { fn parse_references(refs: &[Cow<'_, str>]) -> SigningResult> { refs.iter() .map(|addr| SolanaAddress::from_str(addr).map_err(SigningError::from)) - .collect() + .collect::>>() + .context("Invalid transaction reference(s)") } } @@ -544,7 +618,7 @@ impl RawMessageBuilder { match raw_message.message { RawMessageType::legacy(ref legacy) => Self::build_legacy(legacy), RawMessageType::v0(ref v0) => Self::build_v0(v0), - RawMessageType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + RawMessageType::None => SigningError::err(SigningErrorType::Error_invalid_params), } } @@ -553,9 +627,17 @@ impl RawMessageBuilder { ) -> SigningResult { let mut key_signs = PubkeySignatureMap::with_capacity(raw_message.signatures.len()); for entry in raw_message.signatures.iter() { - let pubkey = SolanaAddress::from_str(entry.pubkey.as_ref())?; - let signature = Signature::from_str(entry.signature.as_ref())?; - let ed25519_signature = ed25519::Signature::try_from(signature.0.as_slice())?; + let pubkey = SolanaAddress::from_str(entry.pubkey.as_ref()) + .into_tw() + .context("Invalid 'PubkeySignature::public'")?; + + let signature = Signature::from_str(entry.signature.as_ref()) + .context("Invalid 'PubkeySignature::signature'")?; + + let ed25519_signature = ed25519::Signature::try_from(signature.0.as_slice()) + .into_tw() + .context("Invalid 'PubkeySignature::signature'")?; + key_signs.insert(pubkey, ed25519_signature); } Ok(key_signs) @@ -566,7 +648,10 @@ impl RawMessageBuilder { ) -> SigningResult { let header = Self::build_message_header(&legacy.header)?; let account_keys = Self::build_account_keys(&legacy.account_keys)?; - let recent_blockhash = Blockhash::from_str(legacy.recent_blockhash.as_ref())?; + let recent_blockhash = Blockhash::from_str(legacy.recent_blockhash.as_ref()) + .into_tw() + .context("Invalid recent blockhash")?; + let instructions: Vec<_> = Self::build_instructions(&legacy.instructions)?; Ok(VersionedMessage::Legacy(legacy::Message { @@ -580,7 +665,10 @@ impl RawMessageBuilder { fn build_v0(v0: &Proto::mod_RawMessage::MessageV0) -> SigningResult { let header = Self::build_message_header(&v0.header)?; let account_keys = Self::build_account_keys(&v0.account_keys)?; - let recent_blockhash = Blockhash::from_str(v0.recent_blockhash.as_ref())?; + let recent_blockhash = Blockhash::from_str(v0.recent_blockhash.as_ref()) + .into_tw() + .context("Invalid recent blockhash")?; + let instructions: Vec<_> = Self::build_instructions(&v0.instructions)?; let address_table_lookups = v0 .address_table_lookups @@ -602,7 +690,9 @@ impl RawMessageBuilder { ) -> SigningResult { let raw_header = raw_header .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No message header provided")?; + Ok(MessageHeader { num_required_signatures: try_into_u8(raw_header.num_required_signatures)?, num_readonly_signed_accounts: try_into_u8(raw_header.num_readonly_signed_accounts)?, @@ -616,6 +706,7 @@ impl RawMessageBuilder { .map(|s| SolanaAddress::from_str(s.as_ref())) .collect::>>() .map_err(SigningError::from) + .context("Cannot build account keys") } fn build_instructions( @@ -631,10 +722,11 @@ impl RawMessageBuilder { .accounts .iter() .map(|idx| try_into_u8(*idx)) - .collect::>>()?; + .collect::>>() + .context("Cannot build account metas")?; Ok(CompiledInstruction { - program_id_index: try_into_u8(ix.program_id)?, + program_id_index: try_into_u8(ix.program_id).context("Invalid program ID")?, accounts, data: ix.program_data.to_vec(), }) @@ -643,19 +735,25 @@ impl RawMessageBuilder { fn build_address_lookup_table( lookup: &Proto::mod_RawMessage::MessageAddressTableLookup, ) -> SigningResult { - let account_key = SolanaAddress::from_str(lookup.account_key.as_ref())?; + let account_key = SolanaAddress::from_str(lookup.account_key.as_ref()) + .into_tw() + .context("Invalid lookup's account key")?; + let writable_indexes = lookup .writable_indexes .iter() .copied() .map(try_into_u8) - .collect::>>()?; + .collect::>>() + .context("Invalid writable index(s)")?; + let readonly_indexes = lookup .readonly_indexes .iter() .copied() .map(try_into_u8) - .collect::>>()?; + .collect::>>() + .context("Invalid readonly index(s)")?; Ok(v0::MessageAddressTableLookup { account_key, @@ -669,5 +767,5 @@ fn try_into_u8(num: T) -> SigningResult where u8: TryFrom, { - u8::try_from(num).map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big) } diff --git a/rust/chains/tw_solana/src/modules/proto_builder.rs b/rust/chains/tw_solana/src/modules/proto_builder.rs index d5394ab972b..d40ff7ca664 100644 --- a/rust/chains/tw_solana/src/modules/proto_builder.rs +++ b/rust/chains/tw_solana/src/modules/proto_builder.rs @@ -5,13 +5,12 @@ use crate::transaction::v0; use crate::transaction::versioned::{VersionedMessage, VersionedTransaction}; use std::borrow::Cow; -use tw_coin_entry::error::SigningResult; use tw_proto::Solana::Proto::{self, mod_RawMessage::OneOfmessage as ProtoMessageType}; pub struct ProtoBuilder; impl ProtoBuilder { - pub fn build_from_tx(tx: &VersionedTransaction) -> SigningResult> { + pub fn build_from_tx(tx: &VersionedTransaction) -> Proto::RawMessage<'static> { let message_header = Proto::mod_RawMessage::MessageHeader { num_required_signatures: tx.message.header().num_required_signatures as u32, num_readonly_signed_accounts: tx.message.header().num_readonly_signed_accounts as u32, @@ -61,10 +60,10 @@ impl ProtoBuilder { }, }; - Ok(Proto::RawMessage { + Proto::RawMessage { signatures: Self::build_signatures(tx), message, - }) + } } fn build_address_table_lookups( diff --git a/rust/chains/tw_solana/src/modules/transaction_decoder.rs b/rust/chains/tw_solana/src/modules/transaction_decoder.rs index 00bea7705b2..3a98fb58203 100644 --- a/rust/chains/tw_solana/src/modules/transaction_decoder.rs +++ b/rust/chains/tw_solana/src/modules/transaction_decoder.rs @@ -5,7 +5,7 @@ use crate::modules::proto_builder::ProtoBuilder; use crate::transaction::versioned::VersionedTransaction; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::transaction_decoder::TransactionDecoder; use tw_coin_entry::signing_output_error; use tw_proto::Solana::Proto; @@ -27,8 +27,9 @@ impl SolanaTransactionDecoder { tx: &[u8], ) -> SigningResult> { let decoded_tx: VersionedTransaction = bincode::deserialize(tx) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; - let transaction = ProtoBuilder::build_from_tx(&decoded_tx)?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error decoding transaction as 'bincode'")?; + let transaction = ProtoBuilder::build_from_tx(&decoded_tx); Ok(Proto::DecodingTransactionOutput { transaction: Some(transaction), diff --git a/rust/chains/tw_solana/src/modules/tx_signer.rs b/rust/chains/tw_solana/src/modules/tx_signer.rs index f1bb7c2a0fd..5fb421e6876 100644 --- a/rust/chains/tw_solana/src/modules/tx_signer.rs +++ b/rust/chains/tw_solana/src/modules/tx_signer.rs @@ -6,7 +6,7 @@ use crate::address::SolanaAddress; use crate::modules::PubkeySignatureMap; use crate::transaction::{versioned, Signature}; use std::collections::HashMap; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_keypair::ed25519; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_memory::Data; @@ -44,8 +44,11 @@ impl TxSigner { ) -> SigningResult { let mut tx = versioned::VersionedTransaction::unsigned(unsigned_msg); - if key_signs.len() != tx.message.num_required_signatures() { - return Err(SigningError(SigningErrorType::Error_signatures_count)); + let actual_signatures = key_signs.len(); + let expected_signatures = tx.message.num_required_signatures(); + if actual_signatures != expected_signatures { + return SigningError::err(SigningErrorType::Error_signatures_count) + .with_context(|| format!("Expected '{expected_signatures}' signatures, provided '{actual_signatures}'")); } for (signing_pubkey, ed25519_signature) in key_signs { @@ -53,11 +56,15 @@ impl TxSigner { let account_index = tx .message .get_account_index(signing_pubkey) - .ok_or(SigningError(SigningErrorType::Error_missing_private_key))?; + .or_tw_err(SigningErrorType::Error_missing_private_key) + .with_context(|| { + format!("Provided a signature for an unexpected account: {signing_pubkey}") + })?; let signature_to_reassign = tx .signatures .get_mut(account_index) - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Internal error: invalid number of Tx.signatures[]")?; *signature_to_reassign = Signature(ed25519_signature.to_bytes()); } @@ -65,6 +72,8 @@ impl TxSigner { } pub fn preimage_versioned(msg: &versioned::VersionedMessage) -> SigningResult { - bincode::serialize(&msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) + bincode::serialize(&msg) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Solana Message as 'bincode'") } } diff --git a/rust/chains/tw_solana/src/modules/utils.rs b/rust/chains/tw_solana/src/modules/utils.rs index c6b3d713935..0bf4655d161 100644 --- a/rust/chains/tw_solana/src/modules/utils.rs +++ b/rust/chains/tw_solana/src/modules/utils.rs @@ -8,7 +8,7 @@ use crate::modules::PubkeySignatureMap; use crate::transaction::versioned::VersionedTransaction; use crate::SOLANA_ALPHABET; use std::borrow::Cow; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::{base58, base64}; use tw_hash::H256; @@ -36,13 +36,13 @@ impl SolanaTransaction { let is_url = false; let tx_bytes = base64::decode(encoded_tx, is_url)?; - let tx_to_sign: VersionedTransaction = bincode::deserialize(&tx_bytes) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + let tx_to_sign: VersionedTransaction = + bincode::deserialize(&tx_bytes).map_err(|_| SigningErrorType::Error_input_parse)?; let mut msg_to_sign = tx_to_sign.message; let new_blockchain_hash = base58::decode(recent_blockhash, &SOLANA_ALPHABET)?; let new_blockchain_hash = H256::try_from(new_blockchain_hash.as_slice()) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params)?; // Update the transaction's blockhash and re-sign it. msg_to_sign.set_recent_blockhash(new_blockchain_hash); @@ -64,8 +64,8 @@ impl SolanaTransaction { }; let unsigned_encoded = base64::encode(&unsigned_encoded, is_url); - let signed_encoded = bincode::serialize(&signed_tx) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signed_encoded = + bincode::serialize(&signed_tx).tw_err(|_| SigningErrorType::Error_internal)?; let signed_encoded = base64::encode(&signed_encoded, is_url); Ok(Proto::SigningOutput { diff --git a/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs b/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs index 31d45860f84..041d50012ac 100644 --- a/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs +++ b/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs @@ -6,7 +6,7 @@ use crate::modules::proto_builder::ProtoBuilder; use crate::modules::wallet_connect::request::SignTransactionRequest; use crate::transaction::versioned::VersionedTransaction; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::wallet_connector::WalletConnector; use tw_coin_entry::signing_output_error; use tw_proto::Solana::Proto; @@ -36,7 +36,8 @@ impl SolanaWalletConnector { WCProto::Method::SolanaSignTransaction => { Self::parse_sign_transaction_request(coin, request) }, - _ => Err(SigningError(SigningErrorType::Error_not_supported)), + _ => SigningError::err(SigningErrorType::Error_not_supported) + .context("Unknown WalletConnect method"), } } @@ -45,12 +46,15 @@ impl SolanaWalletConnector { request: WCProto::ParseRequestInput<'_>, ) -> SigningResult> { let sign_req: SignTransactionRequest = serde_json::from_str(&request.payload) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error parsing WalletConnect signing request as JSON")?; + let transaction: VersionedTransaction = bincode::deserialize(&sign_req.transaction.0) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error deserializing Solana transaction as 'bincode'")?; let signing_input = Proto::SigningInput { - raw_message: Some(ProtoBuilder::build_from_tx(&transaction)?), + raw_message: Some(ProtoBuilder::build_from_tx(&transaction)), ..Proto::SigningInput::default() }; diff --git a/rust/chains/tw_solana/src/program/stake_program.rs b/rust/chains/tw_solana/src/program/stake_program.rs index 5161edec832..37ac2e794fb 100644 --- a/rust/chains/tw_solana/src/program/stake_program.rs +++ b/rust/chains/tw_solana/src/program/stake_program.rs @@ -5,7 +5,7 @@ use crate::address::SolanaAddress; use crate::blockhash::Blockhash; use crate::defined_addresses::*; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::sha2::sha256; use tw_hash::H256; diff --git a/rust/chains/tw_solana/src/signer.rs b/rust/chains/tw_solana/src/signer.rs index b03ccd345d0..11b3e8bd2f9 100644 --- a/rust/chains/tw_solana/src/signer.rs +++ b/rust/chains/tw_solana/src/signer.rs @@ -8,7 +8,7 @@ use crate::modules::tx_signer::TxSigner; use crate::SOLANA_ALPHABET; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::{base58, base64}; use tw_proto::Solana::Proto; @@ -39,14 +39,16 @@ impl SolanaSigner { let unsigned_msg = builder.build()?; let encoded_unsigned = bincode::serialize(&unsigned_msg) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Solana Message as 'bincode'")?; let encoded_unsigned = encode(&encoded_unsigned); let signed_tx = TxSigner::sign_versioned(unsigned_msg, &signing_keys, &external_signatures)?; let encoded_tx = bincode::serialize(&signed_tx) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Solana Transaction as 'bincode'")?; let encoded_tx = encode(&encoded_tx); Ok(Proto::SigningOutput { diff --git a/rust/chains/tw_solana/src/transaction/mod.rs b/rust/chains/tw_solana/src/transaction/mod.rs index b5848ba006e..7ba4da1ac63 100644 --- a/rust/chains/tw_solana/src/transaction/mod.rs +++ b/rust/chains/tw_solana/src/transaction/mod.rs @@ -6,7 +6,7 @@ use crate::SOLANA_ALPHABET; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58; use tw_hash::{as_byte_sequence, H512}; @@ -54,10 +54,12 @@ impl FromStr for Signature { fn from_str(s: &str) -> Result { let data = base58::decode(s, &SOLANA_ALPHABET) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error decoding Solana Signature from base58")?; H512::try_from(data.as_slice()) .map(Signature) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse)) + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Solana Signature must be 64 byte length") } } diff --git a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs index a4a4cd1c08d..6dffaa32a63 100644 --- a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs +++ b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58; use tw_solana::modules::utils::SolanaTransaction; use tw_solana::SOLANA_ALPHABET; diff --git a/rust/chains/tw_sui/src/address.rs b/rust/chains/tw_sui/src/address.rs index 76c359561ec..1ed70526d86 100644 --- a/rust/chains/tw_sui/src/address.rs +++ b/rust/chains/tw_sui/src/address.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_hash::blake2::blake2_b; use tw_keypair::ed25519; diff --git a/rust/chains/tw_sui/src/compiler.rs b/rust/chains/tw_sui/src/compiler.rs index adff5bd13d6..90b95ec7a6b 100644 --- a/rust/chains/tw_sui/src/compiler.rs +++ b/rust/chains/tw_sui/src/compiler.rs @@ -9,7 +9,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::base64; use tw_keypair::ed25519; diff --git a/rust/chains/tw_sui/src/entry.rs b/rust/chains/tw_sui/src/entry.rs index 70682041a43..72ec4866299 100644 --- a/rust/chains/tw_sui/src/entry.rs +++ b/rust/chains/tw_sui/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_sui/src/modules/tx_builder.rs b/rust/chains/tw_sui/src/modules/tx_builder.rs index 58ec6dff986..44ce73e43d6 100644 --- a/rust/chains/tw_sui/src/modules/tx_builder.rs +++ b/rust/chains/tw_sui/src/modules/tx_builder.rs @@ -8,7 +8,7 @@ use crate::transaction::transaction_builder::TransactionBuilder; use crate::transaction::transaction_data::TransactionData; use std::borrow::Cow; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64; use tw_keypair::ed25519; use tw_keypair::traits::KeyPairTrait; @@ -53,7 +53,7 @@ impl<'a> TWTransactionBuilder<'a> { TransactionType::transfer_object(ref transfer_obj) => { self.transfer_object_from_proto(transfer_obj) }, - TransactionType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + TransactionType::None => SigningError::err(SigningErrorType::Error_invalid_params), }?; Ok(TWTransaction::Transaction(tx_data)) } @@ -61,13 +61,15 @@ impl<'a> TWTransactionBuilder<'a> { fn sign_direct_from_proto(&self, sign_direct: &Proto::SignDirect<'_>) -> SigningResult { let url = false; base64::decode(&sign_direct.unsigned_tx_msg, url) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse)) + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error parsing Raw Unsigned TX message as base64") } fn pay_sui_from_proto(&self, pay_sui: &Proto::PaySui<'_>) -> SigningResult { let signer = self.signer_address()?; let input_coins = Self::build_coins(&pay_sui.input_coins)?; - let recipients = Self::parse_addresses(&pay_sui.recipients)?; + let recipients = Self::parse_addresses(&pay_sui.recipients) + .context("Invalid one of the recipients addresses")?; TransactionBuilder::pay_sui( signer, @@ -85,7 +87,9 @@ impl<'a> TWTransactionBuilder<'a> { ) -> SigningResult { let signer = self.signer_address()?; let input_coins = Self::build_coins(&pay_all_sui.input_coins)?; - let recipient = SuiAddress::from_str(&pay_all_sui.recipient)?; + let recipient = SuiAddress::from_str(&pay_all_sui.recipient) + .into_tw() + .context("Invalid recipient address")?; TransactionBuilder::pay_all_sui( signer, @@ -99,8 +103,9 @@ impl<'a> TWTransactionBuilder<'a> { fn pay_from_proto(&self, pay: &Proto::Pay<'_>) -> SigningResult { let signer = self.signer_address()?; let input_coins = Self::build_coins(&pay.input_coins)?; - let recipients = Self::parse_addresses(&pay.recipients)?; - let gas = Self::require_coin(&pay.gas)?; + let recipients = Self::parse_addresses(&pay.recipients) + .context("Invalid one of the recipients addresses")?; + let gas = Self::require_coin(&pay.gas).context("No 'gas' coin specified")?; TransactionBuilder::pay( signer, @@ -121,8 +126,10 @@ impl<'a> TWTransactionBuilder<'a> { let input_coins = Self::build_coins(&stake.coins)?; let amount = stake.amount.as_ref().map(|a| a.amount); - let validator = SuiAddress::from_str(stake.validator.as_ref())?; - let gas = Self::require_coin(&stake.gas)?; + let validator = SuiAddress::from_str(stake.validator.as_ref()) + .into_tw() + .context("Invalid validator address")?; + let gas = Self::require_coin(&stake.gas).context("No 'gas' coin specified")?; TransactionBuilder::request_add_stake( signer, @@ -141,8 +148,9 @@ impl<'a> TWTransactionBuilder<'a> { ) -> SigningResult { let signer = self.signer_address()?; - let staked_sui = Self::require_coin(&withdraw.staked_sui)?; - let gas = Self::require_coin(&withdraw.gas)?; + let staked_sui = + Self::require_coin(&withdraw.staked_sui).context("No 'staked_sui' coin specified")?; + let gas = Self::require_coin(&withdraw.gas).context("No 'gas' coin specified")?; TransactionBuilder::request_withdraw_stake( signer, @@ -159,9 +167,11 @@ impl<'a> TWTransactionBuilder<'a> { ) -> SigningResult { let signer = self.signer_address()?; - let recipient = SuiAddress::from_str(&transfer_obj.recipient)?; - let object = Self::require_coin(&transfer_obj.object)?; - let gas = Self::require_coin(&transfer_obj.gas)?; + let recipient = SuiAddress::from_str(&transfer_obj.recipient) + .into_tw() + .context("Invalid recipient address")?; + let object = Self::require_coin(&transfer_obj.object).context("No 'object' specified")?; + let gas = Self::require_coin(&transfer_obj.gas).context("No 'gas' coin specified")?; TransactionBuilder::transfer_object( signer, @@ -175,7 +185,9 @@ impl<'a> TWTransactionBuilder<'a> { fn signer_address(&self) -> SigningResult { if self.input.private_key.is_empty() { - SuiAddress::from_str(&self.input.signer).map_err(SigningError::from) + SuiAddress::from_str(&self.input.signer) + .into_tw() + .context("Invalid signer address") } else { let keypair = self.signer_key()?; SuiAddress::with_ed25519_pubkey(keypair.public()).map_err(SigningError::from) @@ -189,12 +201,12 @@ impl<'a> TWTransactionBuilder<'a> { fn require_coin(maybe_coin: &Option) -> SigningResult { let coin = maybe_coin .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; Self::build_coin(coin) } fn build_coin(coin: &Proto::ObjectRef) -> SigningResult { - let object_id = ObjectID::from_str(coin.object_id.as_ref())?; + let object_id = ObjectID::from_str(coin.object_id.as_ref()).context("Invalid Object ID")?; let version = SequenceNumber(coin.version); let object_digest = ObjectDigest::from_str(coin.object_digest.as_ref())?; diff --git a/rust/chains/tw_sui/src/modules/tx_signer.rs b/rust/chains/tw_sui/src/modules/tx_signer.rs index e7364feac56..34eb476f48b 100644 --- a/rust/chains/tw_sui/src/modules/tx_signer.rs +++ b/rust/chains/tw_sui/src/modules/tx_signer.rs @@ -7,7 +7,7 @@ use crate::signature::SuiSignatureInfo; use crate::transaction::transaction_data::TransactionData; use serde::Serialize; use serde_repr::Serialize_repr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; use tw_hash::blake2::blake2_b; use tw_hash::H256; @@ -88,11 +88,13 @@ impl TxSigner { let public_key = signer_key.public(); let signer_address = SuiAddress::with_ed25519_pubkey(public_key)?; if signer_address != tx.sender() { - return Err(SigningError(SigningErrorType::Error_missing_private_key)); + return SigningError::err(SigningErrorType::Error_missing_private_key) + .context("Given private key does not belong to the sender address"); } - let unsigned_tx_data = - bcs::encode(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let unsigned_tx_data = bcs::encode(tx) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing TransactionData")?; Self::sign_direct(unsigned_tx_data, signer_key) } @@ -107,8 +109,9 @@ impl TxSigner { } pub fn preimage(tx: &TransactionData) -> SigningResult { - let unsigned_tx_data = - bcs::encode(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let unsigned_tx_data = bcs::encode(tx) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing TransactionData")?; Self::preimage_direct(unsigned_tx_data) } @@ -118,8 +121,9 @@ impl TxSigner { version: IntentVersion::V0, app_id: AppId::Sui, }; - let intent_data = - bcs::encode(&intent).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let intent_data = bcs::encode(&intent) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Intent message")?; let tx_data_to_sign: Data = intent_data .into_iter() @@ -127,7 +131,7 @@ impl TxSigner { .collect(); let tx_hash_to_sign = blake2_b(&tx_data_to_sign, H256::LEN) .and_then(|hash| H256::try_from(hash.as_slice())) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal)?; Ok(TransactionPreimage { unsigned_tx_data, diff --git a/rust/chains/tw_sui/src/signer.rs b/rust/chains/tw_sui/src/signer.rs index bfb59285678..46546e6e356 100644 --- a/rust/chains/tw_sui/src/signer.rs +++ b/rust/chains/tw_sui/src/signer.rs @@ -6,7 +6,7 @@ use crate::modules::tx_builder::{TWTransaction, TWTransactionBuilder}; use crate::modules::tx_signer::TxSigner; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::base64; use tw_proto::Sui::Proto; diff --git a/rust/chains/tw_sui/src/transaction/programmable_transaction.rs b/rust/chains/tw_sui/src/transaction/programmable_transaction.rs index 6a3529299b5..82445cae86e 100644 --- a/rust/chains/tw_sui/src/transaction/programmable_transaction.rs +++ b/rust/chains/tw_sui/src/transaction/programmable_transaction.rs @@ -9,7 +9,7 @@ use indexmap::IndexMap; use move_core_types::identifier::Identifier; use move_core_types::language_storage::TypeTag; use serde::{Deserialize, Serialize}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; /// A series of commands where the results of one command can be used in future @@ -54,7 +54,8 @@ impl ProgrammableTransactionBuilder { let mut coins = coins.into_iter(); let Some(coin) = coins.next() else { // coins vector is empty - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No coins provided"); }; let coin_arg = self.obj(ObjectArg::ImmOrOwnedObject(coin))?; let merge_args: Vec<_> = coins @@ -140,7 +141,10 @@ impl ProgrammableTransactionBuilder { let id = obj_arg.id(); let obj_arg = if let Some(old_value) = self.inputs.get(&BuilderArg::Object(id)) { let old_obj_arg = match old_value { - CallArg::Pure(_) => return Err(SigningError(SigningErrorType::Error_internal)), + CallArg::Pure(_) => { + return SigningError::err(SigningErrorType::Error_internal) + .context("Expected Object, found Pure") + }, CallArg::Object(arg) => arg, }; match (old_obj_arg, obj_arg) { @@ -158,7 +162,8 @@ impl ProgrammableTransactionBuilder { ) if v1 == &v2 => { if id1 != &id2 || id != id2 { // "invariant violation! object has id does not match call arg" - return Err(SigningError(SigningErrorType::Error_internal)); + return SigningError::err(SigningErrorType::Error_internal) + .context("invariant violation! object has id does not match call arg"); } ObjectArg::SharedObject { id, @@ -168,8 +173,8 @@ impl ProgrammableTransactionBuilder { }, (old_obj_arg, obj_arg) => { if old_obj_arg != &obj_arg { - // "Mismatched Object argument kind for object {id}. {old_value:?} is not compatible with {obj_arg:?}" - return Err(SigningError(SigningErrorType::Error_internal)); + return SigningError::err(SigningErrorType::Error_internal) + .with_context(|| format!("Mismatched Object argument kind for object {id:?}. {old_value:?} is not compatible with {obj_arg:?}")); } obj_arg }, @@ -208,7 +213,11 @@ impl ProgrammableTransactionBuilder { ) -> SigningResult<()> { if recipients.len() != amounts.len() { // "Recipients and amounts mismatch. Got {} recipients but {} amounts" - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params).with_context(|| { + let recipients_num = recipients.len(); + let amounts_num = amounts.len(); + format!("Recipients and amounts mismatch. Got {recipients_num} recipients but {amounts_num} amounts") + }); } if amounts.is_empty() { return Ok(()); @@ -224,7 +233,8 @@ impl ProgrammableTransactionBuilder { } let Argument::Result(split_primary) = self.command(Command::SplitCoins(coin, amt_args)) else { - panic!("self.command should always give a Argument::Result") + return SigningError::err(SigningErrorType::Error_internal) + .context("self.command should always give an Argument::Result"); }; for (recipient, split_secondaries) in recipient_map { let rec_arg = self.pure(recipient).unwrap(); diff --git a/rust/chains/tw_sui/src/transaction/sui_types.rs b/rust/chains/tw_sui/src/transaction/sui_types.rs index b35df1b26d8..625e6bde69e 100644 --- a/rust/chains/tw_sui/src/transaction/sui_types.rs +++ b/rust/chains/tw_sui/src/transaction/sui_types.rs @@ -7,7 +7,7 @@ use crate::constants::{SUI_SYSTEM_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_SHARE use move_core_types::account_address::AccountAddress; use serde::{Deserialize, Serialize}; use std::str::FromStr; -use tw_coin_entry::error::{AddressError, SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58::{self, Alphabet}; use tw_hash::{as_bytes, H256}; use tw_memory::Data; @@ -22,10 +22,12 @@ pub struct SequenceNumber(pub u64); pub struct ObjectID(pub AccountAddress); impl FromStr for ObjectID { - type Err = AddressError; + type Err = SigningError; fn from_str(s: &str) -> Result { - let addr = SuiAddress::from_str(s)?; + let addr = SuiAddress::from_str(s) + .into_tw() + .context("Invalid Object ID")?; Ok(ObjectID(addr.into_inner())) } } @@ -38,10 +40,12 @@ impl FromStr for ObjectDigest { fn from_str(s: &str) -> Result { let bytes = base58::decode(s, Alphabet::BITCOIN) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid Object Digest: expected valid base58 string")?; H256::try_from(bytes.as_slice()) .map(ObjectDigest) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid Object Digest: expected exactly 32 bytes") } } diff --git a/rust/chains/tw_sui/src/transaction/transaction_builder.rs b/rust/chains/tw_sui/src/transaction/transaction_builder.rs index 93ad29060f0..3c7309013fb 100644 --- a/rust/chains/tw_sui/src/transaction/transaction_builder.rs +++ b/rust/chains/tw_sui/src/transaction/transaction_builder.rs @@ -11,7 +11,7 @@ use crate::transaction::command::Command; use crate::transaction::programmable_transaction::ProgrammableTransactionBuilder; use crate::transaction::sui_types::{CallArg, ObjectArg, ObjectRef}; use crate::transaction::transaction_data::{TransactionData, TransactionKind}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; pub struct TransactionBuilder; @@ -94,8 +94,9 @@ impl TransactionBuilder { gas_price: u64, ) -> SigningResult { if input_coins.iter().any(|coin| coin.0 == gas.0) { - // Gas coin is in input coins of Pay transaction, use PaySui transaction instead!. - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params).context( + "Gas coin is in input coins of Pay transaction, use PaySui transaction instead!", + ); } TransactionData::new_pay( @@ -121,8 +122,8 @@ impl TransactionBuilder { gas_price: u64, ) -> SigningResult { if input_coins.is_empty() { - // "Empty input coins for Pay related transaction" - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Empty input coins for Pay related transaction"); } let gas_object_ref = input_coins.remove(0); @@ -148,8 +149,8 @@ impl TransactionBuilder { gas_price: u64, ) -> SigningResult { if input_coins.is_empty() { - // "Empty input coins for Pay related transaction" - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Empty input coins for Pay related transaction"); } let gas_object_ref = input_coins.remove(0); diff --git a/rust/chains/tw_sui/src/transaction/transaction_data.rs b/rust/chains/tw_sui/src/transaction/transaction_data.rs index 6f3329cc419..bfe18fa4e3a 100644 --- a/rust/chains/tw_sui/src/transaction/transaction_data.rs +++ b/rust/chains/tw_sui/src/transaction/transaction_data.rs @@ -10,7 +10,7 @@ use crate::transaction::sui_types::{CallArg, GasData, ObjectID, ObjectRef, Trans use move_core_types::identifier::Identifier; use move_core_types::language_storage::TypeTag; use serde::{Deserialize, Serialize}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; #[derive(Debug, Deserialize, Serialize)] pub enum TransactionData { diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 25f0316984a..970701b0d14 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/tw_any_coin/src/any_address.rs b/rust/tw_any_coin/src/any_address.rs index 4716d920da3..f0e83b169c8 100644 --- a/rust/tw_any_coin/src/any_address.rs +++ b/rust/tw_any_coin/src/any_address.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::prefix::AddressPrefix; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; diff --git a/rust/tw_any_coin/src/any_signer.rs b/rust/tw_any_coin/src/any_signer.rs index 2e7b3692fcf..014e8a0c736 100644 --- a/rust/tw_any_coin/src/any_signer.rs +++ b/rust/tw_any_coin/src/any_signer.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/message_signer.rs b/rust/tw_any_coin/src/message_signer.rs index 5ae2f83bc8f..89297a59680 100644 --- a/rust/tw_any_coin/src/message_signer.rs +++ b/rust/tw_any_coin/src/message_signer.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/transaction_compiler.rs b/rust/tw_any_coin/src/transaction_compiler.rs index b25d472f858..d20e9ee1cef 100644 --- a/rust/tw_any_coin/src/transaction_compiler.rs +++ b/rust/tw_any_coin/src/transaction_compiler.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/transaction_decoder.rs b/rust/tw_any_coin/src/transaction_decoder.rs index fcc10c5dec4..13c40fd7512 100644 --- a/rust/tw_any_coin/src/transaction_decoder.rs +++ b/rust/tw_any_coin/src/transaction_decoder.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/wallet_connect_request.rs b/rust/tw_any_coin/src/wallet_connect_request.rs index 72465e79ccd..22099edad58 100644 --- a/rust/tw_any_coin/src/wallet_connect_request.rs +++ b/rust/tw_any_coin/src/wallet_connect_request.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs index b2016790f38..d46b6db874f 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs @@ -10,7 +10,7 @@ use crate::chains::aptos::APTOS_COIN_TYPE; use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::ToHex; use tw_keypair::ed25519; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs index 44933138f42..b5ca60631b1 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs @@ -7,7 +7,7 @@ use crate::chains::aptos::test_cases::transfer_b4d62afd::{ }; use crate::chains::aptos::APTOS_COIN_TYPE; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_misc::assert_eq_json; diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs index 384b8b14cfb..2244b152974 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::DecodeHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs index 6b209eb6451..b83500c18e6 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs @@ -7,7 +7,7 @@ use tw_any_coin::ffi::tw_any_signer::tw_any_signer_plan; use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs index 38e72d2a3ab..8379148b394 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs @@ -5,7 +5,7 @@ use tw_any_coin::ffi::tw_message_signer::{ tw_message_signer_pre_image_hashes, tw_message_signer_sign, tw_message_signer_verify, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs index 5299cebb811..31f6aab6394 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs index c9e90d27a5e..b8264bffe65 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_encoding::hex::DecodeHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs index 797b7e9394e..5bfa7c71d51 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs @@ -5,7 +5,7 @@ use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs index 6ca790dbee2..8240be5a756 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs index 2439165caa7..3b4aa79f304 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs @@ -9,7 +9,7 @@ use crate::chains::thorchain::test_cases::send_fd0445af::{ use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::ToHex; use tw_hash::H256; diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs index e65d2ec1ebc..c02744e79f3 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs @@ -6,7 +6,7 @@ use crate::chains::thorchain::test_cases::send_fd0445af::{ signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, PRIVATE_KEY, }; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs b/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs index d2fa28fc914..ba87a777572 100644 --- a/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs +++ b/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_bech32_address/src/bech32_prefix.rs b/rust/tw_bech32_address/src/bech32_prefix.rs index c644ff6046c..fa548edd09a 100644 --- a/rust/tw_bech32_address/src/bech32_prefix.rs +++ b/rust/tw_bech32_address/src/bech32_prefix.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::prefix::AddressPrefix; pub struct Bech32Prefix { diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs index ac5d944054b..220fe1a1b77 100644 --- a/rust/tw_bech32_address/src/lib.rs +++ b/rust/tw_bech32_address/src/lib.rs @@ -8,7 +8,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bech32; use tw_hash::hasher::Hasher; use tw_hash::H160; diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index a2b60b3fbb5..009133153ce 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -1,5 +1,5 @@ use crate::modules::signer::Signer; -use crate::{Error, Result}; +use crate::{bitcoin_output_error, Error, Result}; use bitcoin::address::NetworkChecked; use std::borrow::Cow; use std::fmt::Display; @@ -7,14 +7,13 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; -use tw_coin_entry::signing_output_error; use tw_keypair::tw::PublicKey; use tw_misc::traits::ToBytesVec; use tw_proto::BitcoinV2::Proto; @@ -103,7 +102,7 @@ impl CoinEntry for BitcoinEntry { #[inline] fn sign(&self, _coin: &dyn CoinContext, proto: Self::SigningInput<'_>) -> Self::SigningOutput { Signer::sign_proto(_coin, proto) - .unwrap_or_else(|err| signing_output_error!(Proto::SigningOutput, err)) + .unwrap_or_else(|err| bitcoin_output_error!(Proto::SigningOutput, err)) } #[inline] @@ -113,7 +112,7 @@ impl CoinEntry for BitcoinEntry { proto: Proto::SigningInput<'_>, ) -> Self::PreSigningOutput { self.preimage_hashes_impl(_coin, proto) - .unwrap_or_else(|err| signing_output_error!(Proto::PreSigningOutput, err)) + .unwrap_or_else(|err| bitcoin_output_error!(Proto::PreSigningOutput, err)) } #[inline] @@ -125,7 +124,7 @@ impl CoinEntry for BitcoinEntry { _public_keys: Vec, ) -> Self::SigningOutput { self.compile_impl(_coin, proto, signatures, _public_keys) - .unwrap_or_else(|err| signing_output_error!(Proto::SigningOutput, err)) + .unwrap_or_else(|err| bitcoin_output_error!(Proto::SigningOutput, err)) } #[inline] diff --git a/rust/tw_bitcoin/src/lib.rs b/rust/tw_bitcoin/src/lib.rs index be3e835b6a6..ffbd96a5794 100644 --- a/rust/tw_bitcoin/src/lib.rs +++ b/rust/tw_bitcoin/src/lib.rs @@ -11,6 +11,20 @@ pub use secp256k1; use tw_proto::BitcoinV2::Proto; +/// TODO use the standard `TWError` in tw_bitcoin crate. +#[macro_export] +macro_rules! bitcoin_output_error { + ($output:ty, $error:expr) => {{ + let err = $error; + + let mut output = <$output>::default(); + output.error = err.0; + output.error_message = std::borrow::Cow::from(err.to_string()); + + output + }}; +} + pub type Result = std::result::Result; #[derive(Debug)] diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index b6a4cd10e6b..e9609515d68 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -4,7 +4,7 @@ use crate::coin_context::CoinContext; use crate::derivation::Derivation; -use crate::error::AddressResult; +use crate::error::prelude::*; use crate::modules::json_signer::JsonSigner; use crate::modules::plan_builder::PlanBuilder; use crate::prefix::Prefix; diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index 4bf882a08e0..25d00ca27c2 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -5,8 +5,7 @@ use crate::coin_context::CoinContext; use crate::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; use crate::derivation::Derivation; -use crate::error::SigningResult; -use crate::error::{AddressResult, SigningError, SigningErrorType}; +use crate::error::prelude::*; use crate::modules::json_signer::JsonSigner; use crate::modules::message_signer::MessageSigner; use crate::modules::plan_builder::PlanBuilder; @@ -154,7 +153,7 @@ where private_key: PrivateKeyBytes, ) -> SigningResult { let Some(json_signer) = self.json_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let private_key = PrivateKey::new(private_key)?; @@ -181,7 +180,7 @@ where fn plan(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(plan_builder) = self.plan_builder() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::SigningInput<'_> = deserialize(input)?; @@ -191,7 +190,7 @@ where fn sign_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(message_signer) = self.message_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::MessageSigningInput<'_> = @@ -202,7 +201,7 @@ where fn message_preimage_hashes(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(message_signer) = self.message_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::MessageSigningInput<'_> = @@ -213,7 +212,7 @@ where fn verify_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(message_signer) = self.message_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::MessageVerifyingInput<'_> = @@ -227,7 +226,7 @@ where input: &[u8], ) -> SigningResult { let Some(wc_connector) = self.wallet_connector() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: WCProto::ParseRequestInput = deserialize(input)?; @@ -237,7 +236,7 @@ where fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> SigningResult { let Some(tx_decoder) = self.transaction_decoder() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let output = tx_decoder.decode_transaction(coin, tx); diff --git a/rust/tw_coin_entry/src/common/compile_input.rs b/rust/tw_coin_entry/src/common/compile_input.rs index e079d5a5c7b..27ce0318844 100644 --- a/rust/tw_coin_entry/src/common/compile_input.rs +++ b/rust/tw_coin_entry/src/common/compile_input.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::coin_entry::{PublicKeyBytes, SignatureBytes}; -use crate::error::{SigningError, SigningErrorType, SigningResult}; +use crate::error::prelude::*; pub struct SingleSignaturePubkey { pub signature: SignatureBytes, @@ -16,17 +16,20 @@ impl SingleSignaturePubkey { public_keys: Vec, ) -> SigningResult { if signatures.len() > 1 || public_keys.len() > 1 { - return Err(SigningError(SigningErrorType::Error_no_support_n2n)); + return TWError::err(SigningErrorType::Error_no_support_n2n) + .context("Expected exactly one signature and public key"); } let signature = signatures .into_iter() .next() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Expected exactly one signature and public key")?; let public_key = public_keys .into_iter() .next() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Expected exactly one signature and public key")?; Ok(SingleSignaturePubkey { signature, @@ -36,13 +39,15 @@ impl SingleSignaturePubkey { pub fn from_sign_list(signatures: Vec) -> SigningResult { if signatures.len() > 1 { - return Err(SigningError(SigningErrorType::Error_no_support_n2n)); + return TWError::err(SigningErrorType::Error_no_support_n2n) + .context("Expected exactly one signature"); } let signature = signatures .into_iter() .next() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Expected exactly one signature")?; Ok(SingleSignaturePubkey { signature, diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs deleted file mode 100644 index a8a75e9a2e5..00000000000 --- a/rust/tw_coin_entry/src/error.rs +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use std::fmt; -use std::fmt::Formatter; -use tw_encoding::EncodingError; -use tw_keypair::KeyPairError; -use tw_number::NumberError; -use tw_proto::Common::Proto; -use tw_proto::ProtoError; - -#[macro_export] -macro_rules! signing_output_error { - ($output:ty, $error:expr) => {{ - let err = $error; - - let mut output = <$output>::default(); - output.error = err.0; - output.error_message = std::borrow::Cow::from(err.to_string()); - - output - }}; -} - -pub type AddressResult = Result; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum AddressError { - UnknownCoinType, - Unsupported, - MissingPrefix, - FromHexError, - FromBase58Error, - FromBech32Error, - PublicKeyTypeMismatch, - UnexpectedAddressPrefix, - UnexpectedHasher, - InvalidHrp, - InvalidRegistry, - InvalidInput, - InvalidChecksum, - Internal, -} - -pub type SigningResult = Result; -pub type SigningErrorType = Proto::SigningError; - -/// The wrapper over the `Common::SigningErrorType` error for convenient use. -#[derive(Debug)] -pub struct SigningError(pub SigningErrorType); - -impl From for SigningError { - #[inline] - fn from(_err: NumberError) -> Self { - SigningError(SigningErrorType::Error_invalid_params) - } -} - -impl From for SigningError { - #[inline] - fn from(_err: AddressError) -> Self { - SigningError(SigningErrorType::Error_invalid_address) - } -} - -impl From for SigningError { - fn from(_value: serde_json::Error) -> Self { - SigningError(SigningErrorType::Error_input_parse) - } -} - -impl From for SigningError { - fn from(_e: EncodingError) -> Self { - SigningError(SigningErrorType::Error_input_parse) - } -} - -impl From for SigningError { - fn from(err: KeyPairError) -> Self { - match err { - KeyPairError::InvalidSecretKey => { - SigningError(SigningErrorType::Error_invalid_private_key) - }, - KeyPairError::InvalidPublicKey - | KeyPairError::InvalidSignature - | KeyPairError::InvalidSignMessage - | KeyPairError::SignatureVerifyError => { - SigningError(SigningErrorType::Error_invalid_params) - }, - KeyPairError::SigningError => SigningError(SigningErrorType::Error_signing), - } - } -} - -impl From for SigningError { - fn from(_e: ProtoError) -> Self { - SigningError(SigningErrorType::Error_input_parse) - } -} - -impl fmt::Display for SigningError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let str = match self.0 { - SigningErrorType::OK => "", - SigningErrorType::Error_general => "Unknown error", - SigningErrorType::Error_internal => "Internal error", - SigningErrorType::Error_low_balance => "Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance", - SigningErrorType::Error_zero_amount_requested => "Requested amount is zero, send of 0 makes no sense", - SigningErrorType::Error_missing_private_key => "One required key is missing (too few or wrong keys are provided)", - SigningErrorType::Error_invalid_private_key => "A private key provided is invalid (e.g. wrong size, usually should be 32 bytes)", - SigningErrorType::Error_invalid_address => "A provided address (e.g. destination address) is invalid", - SigningErrorType::Error_invalid_utxo => "A provided input UTXO is invalid", - SigningErrorType::Error_invalid_utxo_amount => "The amount of an input UTXO is invalid", - SigningErrorType::Error_wrong_fee => "Wrong fee is given, probably it is too low to cover minimal fee for the transaction", - SigningErrorType::Error_signing => "General signing error", - SigningErrorType::Error_tx_too_big => "Resulting transaction is too large", - SigningErrorType::Error_missing_input_utxos => "No input UTXOs provided", - SigningErrorType::Error_not_enough_utxos => "Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out)", - SigningErrorType::Error_script_redeem => "Missing required redeem script", - SigningErrorType::Error_script_output => "Invalid required output script", - SigningErrorType::Error_script_witness_program => "Unrecognized witness program", - SigningErrorType::Error_invalid_memo => "Invalid memo", - SigningErrorType::Error_input_parse => "Some input field cannot be parsed", - SigningErrorType::Error_no_support_n2n => "Multi-input and multi-output transaction not supported", - SigningErrorType::Error_signatures_count => "Incorrect count of signatures passed to compile", - SigningErrorType::Error_invalid_params => "Incorrect input parameter", - SigningErrorType::Error_invalid_requested_token_amount => "Invalid input token amount", - SigningErrorType::Error_not_supported => "Operation not supported for the chain", - SigningErrorType::Error_dust_amount_requested => "Requested amount is too low (less dust)", - }; - write!(f, "{str}") - } -} diff --git a/rust/tw_coin_entry/src/error/address_error.rs b/rust/tw_coin_entry/src/error/address_error.rs new file mode 100644 index 00000000000..bfd4231d33b --- /dev/null +++ b/rust/tw_coin_entry/src/error/address_error.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub type AddressResult = Result; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum AddressError { + UnknownCoinType, + Unsupported, + MissingPrefix, + FromHexError, + FromBase58Error, + FromBech32Error, + PublicKeyTypeMismatch, + UnexpectedAddressPrefix, + UnexpectedHasher, + InvalidHrp, + InvalidRegistry, + InvalidInput, + InvalidChecksum, + Internal, +} diff --git a/rust/tw_coin_entry/src/error/impl_from.rs b/rust/tw_coin_entry/src/error/impl_from.rs new file mode 100644 index 00000000000..8af3f926749 --- /dev/null +++ b/rust/tw_coin_entry/src/error/impl_from.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::error::address_error::AddressError; +use crate::error::prelude::*; +use tw_encoding::EncodingError; +use tw_keypair::KeyPairError; +use tw_number::NumberError; +use tw_proto::ProtoError; + +impl From for SigningError { + #[inline] + fn from(_err: NumberError) -> Self { + TWError::new(SigningErrorType::Error_invalid_params) + } +} + +impl From for SigningError { + #[inline] + fn from(_err: AddressError) -> Self { + TWError::new(SigningErrorType::Error_invalid_address) + } +} + +impl From for SigningError { + fn from(_value: serde_json::Error) -> Self { + TWError::new(SigningErrorType::Error_input_parse) + } +} + +impl From for SigningError { + fn from(_e: EncodingError) -> Self { + TWError::new(SigningErrorType::Error_input_parse) + } +} + +impl From for SigningError { + fn from(err: KeyPairError) -> Self { + match err { + KeyPairError::InvalidSecretKey => { + TWError::new(SigningErrorType::Error_invalid_private_key) + }, + KeyPairError::InvalidPublicKey + | KeyPairError::InvalidSignature + | KeyPairError::InvalidSignMessage + | KeyPairError::SignatureVerifyError => { + TWError::new(SigningErrorType::Error_invalid_params) + }, + KeyPairError::SigningError => TWError::new(SigningErrorType::Error_signing), + } + } +} + +impl From for SigningError { + fn from(_e: ProtoError) -> Self { + TWError::new(SigningErrorType::Error_input_parse) + } +} diff --git a/rust/tw_coin_entry/src/error/mod.rs b/rust/tw_coin_entry/src/error/mod.rs new file mode 100644 index 00000000000..3202011e149 --- /dev/null +++ b/rust/tw_coin_entry/src/error/mod.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod address_error; +mod impl_from; +mod tw_error; +mod tw_result; + +pub mod prelude { + pub use super::address_error::*; + pub use super::tw_error::*; + pub use super::tw_result::*; + + pub type SigningErrorType = tw_proto::Common::Proto::SigningError; + pub type SigningError = TWError; + pub type SigningResult = Result; +} + +#[macro_export] +macro_rules! signing_output_error { + ($output:ty, $error:expr) => {{ + let err = $error; + + let mut output = <$output>::default(); + output.error = *TWError::error_type(&err); + output.error_message = std::borrow::Cow::from(err.to_string()); + + output + }}; +} + +#[cfg(test)] +mod tests { + use super::prelude::*; + + fn function_signing_error_type() -> Result<(), SigningErrorType> { + Err(SigningErrorType::Error_internal) + } + + fn function_address_error() -> AddressResult<()> { + Err(AddressError::Internal) + } + + /// Test `AddressError` -> `TWError` conversion via [`Result::context`]. + #[test] + fn test_error_convert_via_context() { + let err: SigningError = function_address_error() + .into_tw() + .context("!test_error_convert_via_context") + .unwrap_err(); + + let expected = r#"A provided address (e.g. destination address) is invalid +Context: +0. !test_error_convert_via_context"#; + assert_eq!(err.to_string(), expected); + } + + /// Test `AddressError` -> `TWError` conversion via [`Result::into_tw`]. + #[test] + fn test_error_convert_via_into_tw() { + let err: SigningError = function_signing_error_type().into_tw().unwrap_err(); + + let expected = r#"Internal error"#; + assert_eq!(err.to_string(), expected); + } + + /// Test error chaining. + #[test] + fn test_error_chaining() { + let res: SigningResult<()> = SigningError::err(SigningErrorType::Error_internal) + .context("First context") + .context("Second context"); + + let expected = r#"Internal error +Context: +0. First context +1. Second context"#; + assert_eq!(res.unwrap_err().to_string(), expected); + } +} diff --git a/rust/tw_coin_entry/src/error/tw_error.rs b/rust/tw_coin_entry/src/error/tw_error.rs new file mode 100644 index 00000000000..55dc3b3f901 --- /dev/null +++ b/rust/tw_coin_entry/src/error/tw_error.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::error::tw_result::TWResult; +use std::fmt; + +/// The wrapper over the `Common::TWErrorKindType` error for convenient use. +pub struct TWError { + error: E, + context: Vec, +} + +impl TWError { + /// Converts `PrevE` into `E` and wraps it as [`TWError`]. + pub fn new(error: PrevE) -> Self + where + E: From, + { + TWError { + error: E::from(error), + context: Vec::default(), + } + } + + /// Converts `PrevE` into `E` and wraps it as [`Err(TWError)`]. + pub fn err(error: PrevE) -> TWResult + where + E: From, + { + Err(TWError::new(error)) + } + + /// Adds an error context. + pub fn context(mut self, context: C) -> Self + where + C: fmt::Display, + { + self.context.push(context.to_string()); + self + } + + /// Returns an inner error type. + pub fn error_type(&self) -> &E { + &self.error + } + + /// Converts [`TWError`] into [`TWError`]. + pub fn map_err(self, f: F) -> TWError + where + F: FnOnce(E) -> NewE, + { + TWError { + error: f(self.error), + context: self.context, + } + } + + fn format_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.context.is_empty() { + return Ok(()); + } + writeln!(f)?; + writeln!(f, "Context:")?; + for (i, context) in self.context.iter().enumerate() { + write!(f, "{i}. {context}")?; + if i < self.context.len() - 1 { + writeln!(f)?; + } + } + Ok(()) + } +} + +impl From for TWError { + fn from(inner: E) -> Self { + TWError::new(inner) + } +} + +impl fmt::Display for TWError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + self.format_context(f) + } +} + +impl fmt::Debug for TWError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.error)?; + self.format_context(f) + } +} diff --git a/rust/tw_coin_entry/src/error/tw_result.rs b/rust/tw_coin_entry/src/error/tw_result.rs new file mode 100644 index 00000000000..01390b58ad1 --- /dev/null +++ b/rust/tw_coin_entry/src/error/tw_result.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::error::prelude::TWError; +use std::fmt; + +pub type TWResult = Result>; + +pub trait ResultContext { + /// Wraps the error value with additional context. + fn context(self, context: C) -> Self + where + C: fmt::Display; + + /// Wraps the error value with additional context that is evaluated lazily + /// only once an error does occur. + fn with_context(self, f: F) -> Self + where + C: fmt::Display, + F: FnOnce() -> C; +} + +pub trait IntoTWError { + /// Wraps the inner `E` error into [`TWError`]. + fn into_tw(self) -> TWResult; +} + +pub trait MapTWError { + /// Maps `PrevE` into [`TWError`] with an `F` mapper. + fn tw_err(self, f: F) -> TWResult + where + F: FnOnce(PrevE) -> E; +} + +pub trait OrTWError { + /// Transforms the [`Option`] into a [`Result>`], mapping [`Some(v)`] to + /// [`Ok(v)`] and [`None`] to [`Err(TWError)`]. + fn or_tw_err(self, error: E) -> TWResult; +} + +impl ResultContext for TWResult { + fn context(self, context: C) -> Self + where + C: fmt::Display, + { + self.map_err(|e| e.context(context)) + } + + fn with_context(self, f: F) -> Self + where + C: fmt::Display, + F: FnOnce() -> C, + { + self.map_err(|e| e.context(f())) + } +} + +impl IntoTWError for Result +where + TWError: From, +{ + fn into_tw(self) -> TWResult { + self.map_err(TWError::from) + } +} + +impl MapTWError for Result { + fn tw_err(self, f: F) -> TWResult + where + F: FnOnce(PrevE) -> E, + { + self.map_err(|e| TWError::new(f(e))) + } +} + +impl OrTWError for Option { + fn or_tw_err(self, error: E) -> TWResult { + self.ok_or(TWError::new(error)) + } +} diff --git a/rust/tw_coin_entry/src/modules/json_signer.rs b/rust/tw_coin_entry/src/modules/json_signer.rs index 3fd0988dae6..9dda8728048 100644 --- a/rust/tw_coin_entry/src/modules/json_signer.rs +++ b/rust/tw_coin_entry/src/modules/json_signer.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; -use crate::error::SigningResult; +use crate::error::prelude::*; use tw_keypair::tw::PrivateKey; pub trait JsonSigner { diff --git a/rust/tw_coin_entry/src/prefix.rs b/rust/tw_coin_entry/src/prefix.rs index c534dc33134..942b7fec832 100644 --- a/rust/tw_coin_entry/src/prefix.rs +++ b/rust/tw_coin_entry/src/prefix.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use crate::error::AddressError; +use crate::error::prelude::*; /// An address prefix. It can contain a bech32 prefix that can be used by `Cosmos` based chains. /// Extend when adding new blockchains. diff --git a/rust/tw_coin_registry/src/error.rs b/rust/tw_coin_registry/src/error.rs index bdd941095ca..ee8f91eaefe 100644 --- a/rust/tw_coin_registry/src/error.rs +++ b/rust/tw_coin_registry/src/error.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; pub type RegistryResult = Result; @@ -16,8 +16,12 @@ impl From for SigningError { #[inline] fn from(e: RegistryError) -> Self { match e { - RegistryError::UnknownCoinType => SigningError(SigningErrorType::Error_invalid_params), - RegistryError::Unsupported => SigningError(SigningErrorType::Error_internal), + RegistryError::UnknownCoinType => { + SigningError::new(SigningErrorType::Error_invalid_params) + .context("Unknown coin type") + }, + RegistryError::Unsupported => SigningError::new(SigningErrorType::Error_internal) + .context("Requested coin type is not supported in Rust yet"), } } } diff --git a/rust/tw_cosmos_sdk/src/address.rs b/rust/tw_cosmos_sdk/src/address.rs index 10cbca9a10c..f860be7ed31 100644 --- a/rust/tw_cosmos_sdk/src/address.rs +++ b/rust/tw_cosmos_sdk/src/address.rs @@ -4,7 +4,7 @@ use serde::Serialize; use std::str::FromStr; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; pub type Address = tw_bech32_address::Bech32Address; pub type Bech32Prefix = tw_bech32_address::bech32_prefix::Bech32Prefix; diff --git a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs index 73b0beafd98..a268e3d7876 100644 --- a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs +++ b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs @@ -5,7 +5,7 @@ use quick_protobuf::MessageWrite; use serde::Serialize; use serde_json::Value as Json; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64::Base64Encoded; use tw_proto::serialize; @@ -46,8 +46,9 @@ impl BroadcastMsg { BroadcastMode::Sync => "sync", } .to_string(); - let tx = - serde_json::to_value(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let tx = serde_json::to_value(tx) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing transaction to sign as JSON")?; Ok(BroadcastMsg::Json { mode, tx }) } diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs index a8310883057..9e2a372dcd5 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs @@ -7,7 +7,7 @@ use crate::modules::serializer::json_serializer::JsonSerializer; use crate::public_key::JsonPublicKey; use crate::transaction::UnsignedTransaction; use std::marker::PhantomData; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::hasher::Hasher; use tw_memory::Data; @@ -30,7 +30,8 @@ where ) -> SigningResult { let tx_to_sign = JsonSerializer::build_unsigned_tx(unsigned)?; let encoded_tx = serde_json::to_string(&tx_to_sign) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing transaction to sign as JSON")?; let tx_hash = hasher.hash(encoded_tx.as_bytes()); Ok(JsonTxPreimage { diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs index dc82a15920a..57f9aea9116 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs @@ -6,7 +6,7 @@ use crate::context::CosmosContext; use crate::modules::serializer::protobuf_serializer::{ProtobufSerializer, SignDirectArgs}; use crate::transaction::UnsignedTransaction; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_hash::hasher::Hasher; use tw_memory::Data; use tw_proto::serialize; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs index 4059f09fbcc..544933f3964 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs @@ -15,7 +15,7 @@ use std::marker::PhantomData; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_misc::traits::ToBytesVec; use tw_proto::Cosmos::Proto; @@ -31,11 +31,8 @@ impl TWTransactionCompiler { coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> CompilerProto::PreSigningOutput<'static> { - match input.signing_mode { - Proto::SigningMode::JSON => Self::preimage_hashes_as_json(coin, input), - Proto::SigningMode::Protobuf => Self::preimage_hashes_as_protobuf(coin, input), - } - .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) } #[inline] @@ -45,16 +42,22 @@ impl TWTransactionCompiler { signatures: Vec, public_keys: Vec, ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + #[inline] + pub(crate) fn preimage_hashes_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { match input.signing_mode { - Proto::SigningMode::JSON => Self::compile_as_json(coin, input, signatures, public_keys), - Proto::SigningMode::Protobuf => { - Self::compile_as_protobuf(coin, input, signatures, public_keys) - }, + Proto::SigningMode::JSON => Self::preimage_hashes_as_json(coin, input), + Proto::SigningMode::Protobuf => Self::preimage_hashes_as_protobuf(coin, input), } - .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) } - pub fn preimage_hashes_as_protobuf( + pub(crate) fn preimage_hashes_as_protobuf( coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> SigningResult> { @@ -79,7 +82,7 @@ impl TWTransactionCompiler { }) } - pub fn preimage_hashes_as_json( + pub(crate) fn preimage_hashes_as_json( coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> SigningResult> { @@ -95,7 +98,22 @@ impl TWTransactionCompiler { }) } - pub fn compile_as_protobuf( + #[inline] + pub(crate) fn compile_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + match input.signing_mode { + Proto::SigningMode::JSON => Self::compile_as_json(coin, input, signatures, public_keys), + Proto::SigningMode::Protobuf => { + Self::compile_as_protobuf(coin, input, signatures, public_keys) + }, + } + } + + pub(crate) fn compile_as_protobuf( coin: &dyn CoinContext, mut input: Proto::SigningInput<'_>, signatures: Vec, @@ -133,7 +151,8 @@ impl TWTransactionCompiler { let signature_json = JsonSerializer::::serialize_signature(&public_key, signature.to_vec()); let signature_json = serde_json::to_string(&[signature_json]) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signatures as JSON")?; Ok(Proto::SigningOutput { signature: Cow::from(signature.to_vec()), @@ -143,7 +162,7 @@ impl TWTransactionCompiler { }) } - pub fn compile_as_json( + pub(crate) fn compile_as_json( coin: &dyn CoinContext, mut input: Proto::SigningInput<'_>, signatures: Vec, @@ -169,7 +188,8 @@ impl TWTransactionCompiler { let broadcast_tx = BroadcastMsg::json(broadcast_mode, &signed_tx_json)?.to_json_string(); let signature_json = serde_json::to_string(&signed_tx_json.signatures) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signatures as JSON")?; Ok(Proto::SigningOutput { signature: Cow::from(signature.to_vec()), diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs index 1bf3dff3649..f38d8e5d9db 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs @@ -9,7 +9,7 @@ use crate::transaction::{Coin, Fee, SignedTransaction, UnsignedTransaction}; use serde::Serialize; use serde_json::Value as Json; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64::Base64Encoded; #[derive(Serialize)] diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs index 150667bb5db..91141c2e1f1 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs @@ -11,7 +11,7 @@ use crate::transaction::{ Coin, Fee, SignMode, SignedTransaction, SignerInfo, TxBody, UnsignedTransaction, }; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::serialize; diff --git a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs index 4a6d4b276c0..5908ae38efa 100644 --- a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs +++ b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs @@ -10,7 +10,7 @@ use crate::public_key::CosmosPublicKey; use std::borrow::Cow; use std::marker::PhantomData; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::Cosmos::Proto; @@ -41,23 +41,14 @@ impl TWSigner { input.public_key = Cow::from(public_key.to_bytes()); let preimage_output = - TWTransactionCompiler::::preimage_hashes(coin, input.clone()); - if preimage_output.error != SigningErrorType::OK { - return Err(SigningError(preimage_output.error)); - } + TWTransactionCompiler::::preimage_hashes_impl(coin, input.clone())?; let signature_data = private_key.sign_tx_hash(&preimage_output.data_hash)?; - let compile_output = TWTransactionCompiler::::compile( + TWTransactionCompiler::::compile_impl( coin, input, vec![signature_data], vec![public_key.to_bytes()], - ); - - if compile_output.error != SigningErrorType::OK { - return Err(SigningError(preimage_output.error)); - } - - Ok(compile_output) + ) } } diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index 8bd97cafbb1..519b4b89dae 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -12,7 +12,7 @@ use crate::transaction::{Coin, Fee, SignMode, SignerInfo, TxBody, UnsignedTransa use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::hasher::Hasher; use tw_keypair::tw; use tw_misc::traits::{OptionalEmpty, ToBytesVec}; @@ -37,7 +37,8 @@ where let fee = input .fee .as_ref() - .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + .or_tw_err(SigningErrorType::Error_wrong_fee) + .context("No fee specified")?; let signer = Self::signer_info_from_proto(coin, input)?; Ok(UnsignedTransaction { @@ -100,7 +101,9 @@ where } fn coin_from_proto(input: &Proto::Amount<'_>) -> SigningResult { - let amount = U256::from_str(&input.amount)?; + let amount = U256::from_str(&input.amount) + .into_tw() + .context("Invalid amount, expected string decimal")?; Ok(Coin { amount, denom: input.denom.to_string(), @@ -112,7 +115,8 @@ where input: &Proto::SigningInput<'_>, ) -> SigningResult { if input.messages.is_empty() { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No TX messages provided"); } let messages = input @@ -201,7 +205,8 @@ where MessageEnum::sign_direct_message(ref _sign) => { // `SignDirect` message must be handled before this function is called. // Consider using `Self::try_sign_direct_args` instead. - Err(SigningError(SigningErrorType::Error_not_supported)) + SigningError::err(SigningErrorType::Error_not_supported) + .context("Consider using `Self::try_sign_direct_args` instead") }, MessageEnum::auth_grant(ref grant) => Self::auth_grant_msg_from_proto(coin, grant), MessageEnum::auth_revoke(ref revoke) => Self::auth_revoke_msg_from_proto(coin, revoke), @@ -215,7 +220,8 @@ where MessageEnum::thorchain_deposit_message(ref deposit) => { Self::thorchain_deposit_msg_from_proto(coin, deposit) }, - MessageEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + MessageEnum::None => SigningError::err(SigningErrorType::Error_invalid_params) + .context("No TX message provided"), } } @@ -232,8 +238,12 @@ where .collect::>()?; let msg = SendMessage { custom_type_prefix: send.type_prefix.to_string().empty_or_some(), - from_address: Address::from_str(&send.from_address)?, - to_address: Address::from_str(&send.to_address)?, + from_address: Address::from_str(&send.from_address) + .into_tw() + .context("Invalid sender address")?, + to_address: Address::from_str(&send.to_address) + .into_tw() + .context("Invalid receiver address")?, amount: amounts, }; Ok(msg.into_boxed()) @@ -248,19 +258,25 @@ where let token = transfer .token .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No token specified")?; let token = Self::coin_from_proto(token)?; let height = transfer .timeout_height .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No timeout height specified")?; let msg = TransferTokensMessage { source_port: transfer.source_port.to_string(), source_channel: transfer.source_channel.to_string(), token, - sender: Address::from_str(&transfer.sender)?, - receiver: Address::from_str(&transfer.receiver)?, + sender: Address::from_str(&transfer.sender) + .into_tw() + .context("Invalid sender address")?, + receiver: Address::from_str(&transfer.receiver) + .into_tw() + .context("Invalid receiver address")?, timeout_height: Height { revision_number: height.revision_number, revision_height: height.revision_height, @@ -279,13 +295,18 @@ where let amount = delegate .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No amount specified")?; let amount = Self::coin_from_proto(amount)?; let msg = DelegateMessage { custom_type_prefix: delegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str(&delegate.delegator_address)?, - validator_address: Address::from_str(&delegate.validator_address)?, + delegator_address: Address::from_str(&delegate.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + validator_address: Address::from_str(&delegate.validator_address) + .into_tw() + .context("Invalid validator address")?, }; Ok(msg.into_boxed()) } @@ -299,14 +320,19 @@ where let amount = undelegate .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No amount specified")?; let amount = Self::coin_from_proto(amount)?; let msg = UndelegateMessage { custom_type_prefix: undelegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str(&undelegate.delegator_address)?, - validator_address: Address::from_str(&undelegate.validator_address)?, + delegator_address: Address::from_str(&undelegate.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + validator_address: Address::from_str(&undelegate.validator_address) + .into_tw() + .context("Invalid validator address")?, }; Ok(msg.into_boxed()) } @@ -319,8 +345,12 @@ where let msg = WithdrawDelegationRewardMessage { custom_type_prefix: withdraw.type_prefix.to_string().empty_or_some(), - delegator_address: Address::from_str(&withdraw.delegator_address)?, - validator_address: Address::from_str(&withdraw.validator_address)?, + delegator_address: Address::from_str(&withdraw.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + validator_address: Address::from_str(&withdraw.validator_address) + .into_tw() + .context("Invalid validator address")?, }; Ok(msg.into_boxed()) } @@ -333,8 +363,12 @@ where let msg = SetWithdrawAddressMessage { custom_type_prefix: set.type_prefix.to_string().empty_or_some(), - delegator_address: Address::from_str(&set.delegator_address)?, - withdraw_address: Address::from_str(&set.withdraw_address)?, + delegator_address: Address::from_str(&set.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + withdraw_address: Address::from_str(&set.withdraw_address) + .into_tw() + .context("Invalid withdraw address")?, }; Ok(msg.into_boxed()) } @@ -348,15 +382,22 @@ where let amount = redelegate .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No amount specified")?; let amount = Self::coin_from_proto(amount)?; - let validator_src_address = Address::from_str(&redelegate.validator_src_address)?; - let validator_dst_address = Address::from_str(&redelegate.validator_dst_address)?; + let validator_src_address = Address::from_str(&redelegate.validator_src_address) + .into_tw() + .context("Invalid source validator address")?; + let validator_dst_address = Address::from_str(&redelegate.validator_dst_address) + .into_tw() + .context("Invalid destination validator address")?; let msg = BeginRedelegateMessage { custom_type_prefix: redelegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str(&redelegate.delegator_address)?, + delegator_address: Address::from_str(&redelegate.delegator_address) + .into_tw() + .context("Invalid delegator address")?, validator_src_address, validator_dst_address, }; @@ -368,7 +409,8 @@ where raw: &Proto::mod_Message::RawJSON<'_>, ) -> SigningResult { let value = serde_json::from_str(&raw.value) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error parsing raw JSON")?; let msg = JsonRawMessage { msg_type: raw.type_pb.to_string(), @@ -385,13 +427,19 @@ where use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecutePayload}; let execute_payload = WasmExecutePayload::Transfer { - amount: U256::from_big_endian_slice(&transfer.amount)?, + amount: U256::from_big_endian_slice(&transfer.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, recipient: transfer.recipient_address.to_string(), }; let msg = TerraExecuteContractMessage { - sender: Address::from_str(&transfer.sender_address)?, - contract: Address::from_str(&transfer.contract_address)?, + sender: Address::from_str(&transfer.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&transfer.contract_address) + .into_tw() + .context("Invalid contract address")?, execute_msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -407,14 +455,20 @@ where use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecutePayload}; let execute_payload = WasmExecutePayload::Send { - amount: U256::from_big_endian_slice(&send.amount)?, + amount: U256::from_big_endian_slice(&send.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, contract: send.recipient_contract_address.to_string(), msg: send.msg.to_string(), }; let msg = TerraExecuteContractMessage { - sender: Address::from_str(&send.sender_address)?, - contract: Address::from_str(&send.contract_address)?, + sender: Address::from_str(&send.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&send.contract_address) + .into_tw() + .context("Invalid contract address")?, execute_msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -436,8 +490,12 @@ where .collect::>()?; let msg = TerraExecuteContractMessage { - sender: Address::from_str(&generic.sender_address)?, - contract: Address::from_str(&generic.contract_address)?, + sender: Address::from_str(&generic.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&generic.contract_address) + .into_tw() + .context("Invalid contract address")?, execute_msg: ExecuteMsg::String(generic.execute_msg.to_string()), coins, }; @@ -453,13 +511,19 @@ where }; let transfer_payload = WasmExecutePayload::Transfer { - amount: U256::from_big_endian_slice(&transfer.amount)?, + amount: U256::from_big_endian_slice(&transfer.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, recipient: transfer.recipient_address.to_string(), }; let msg = WasmExecuteContractMessage { - sender: Address::from_str(&transfer.sender_address)?, - contract: Address::from_str(&transfer.contract_address)?, + sender: Address::from_str(&transfer.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&transfer.contract_address) + .into_tw() + .context("Invalid contract address")?, msg: ExecuteMsg::json(transfer_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -476,14 +540,20 @@ where }; let execute_payload = WasmExecutePayload::Send { - amount: U256::from_big_endian_slice(&send.amount)?, + amount: U256::from_big_endian_slice(&send.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, contract: send.recipient_contract_address.to_string(), msg: send.msg.to_string(), }; let msg = WasmExecuteContractMessage { - sender: Address::from_str(&send.sender_address)?, - contract: Address::from_str(&send.contract_address)?, + sender: Address::from_str(&send.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&send.contract_address) + .into_tw() + .context("Invalid contract address")?, msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -504,8 +574,12 @@ where .collect::>()?; let msg = WasmExecuteContractMessage { - sender: Address::from_str(&generic.sender_address)?, - contract: Address::from_str(&generic.contract_address)?, + sender: Address::from_str(&generic.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&generic.contract_address) + .into_tw() + .context("Invalid contract address")?, msg: ExecuteMsg::String(generic.execute_msg.to_string()), coins, }; @@ -545,16 +619,22 @@ where ProtoGrantType::grant_stake(ref stake) => google::protobuf::Any { type_url: STAKE_AUTHORIZATION_MSG_TYPE.to_string(), value: serialize(stake) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?, + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Error serializing Grant Stake Protobuf message")?, }, ProtoGrantType::None => { - return Err(SigningError(SigningErrorType::Error_invalid_params)) + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No Grant type specified"); }, }; let msg = AuthGrantMessage { - granter: Address::from_str(&auth.granter)?, - grantee: Address::from_str(&auth.grantee)?, + granter: Address::from_str(&auth.granter) + .into_tw() + .context("Invalid granter address")?, + grantee: Address::from_str(&auth.grantee) + .into_tw() + .context("Invalid grantee address")?, grant_msg, expiration_secs: auth.expiration, }; @@ -568,8 +648,12 @@ where use crate::transaction::message::cosmos_auth_message::AuthRevokeMessage; let msg = AuthRevokeMessage { - granter: Address::from_str(&auth.granter)?, - grantee: Address::from_str(&auth.grantee)?, + granter: Address::from_str(&auth.granter) + .into_tw() + .context("Invalid granter address")?, + grantee: Address::from_str(&auth.grantee) + .into_tw() + .context("Invalid grantee address")?, msg_type_url: auth.msg_type_url.to_string(), }; Ok(msg.into_boxed()) @@ -592,7 +676,9 @@ where let msg = VoteMessage { proposal_id: vote.proposal_id, - voter: Address::from_str(&vote.voter)?, + voter: Address::from_str(&vote.voter) + .into_tw() + .context("Invalid voter address")?, option, }; Ok(msg.into_boxed()) @@ -605,8 +691,12 @@ where use crate::transaction::message::stride_message::StrideLiquidStakeMessage; let msg = StrideLiquidStakeMessage { - creator: Address::from_str(&stake.creator)?, - amount: U256::from_str(&stake.amount)?, + creator: Address::from_str(&stake.creator) + .into_tw() + .context("Invalid creator address")?, + amount: U256::from_str(&stake.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, host_denom: stake.host_denom.to_string(), }; Ok(msg.into_boxed()) @@ -620,7 +710,9 @@ where let msg = StrideLiquidRedeemMessage { creator: redeem.creator.to_string(), - amount: U256::from_str(&redeem.amount)?, + amount: U256::from_str(&redeem.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, receiver: redeem.receiver.to_string(), host_zone: redeem.host_zone.to_string(), }; @@ -640,7 +732,8 @@ where let asset_proto = coin_proto .asset .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No Deposit Asset specified")?; let asset = ThorchainAsset { chain: asset_proto.chain.to_string(), @@ -650,7 +743,9 @@ where }; coins.push(ThorchainCoin { asset, - amount: U256::from_str(&coin_proto.amount)?, + amount: U256::from_str(&coin_proto.amount) + .into_tw() + .context("Expected U256 big-endian Deposit amount")?, decimals: coin_proto.decimals, }); } diff --git a/rust/tw_cosmos_sdk/src/private_key/mod.rs b/rust/tw_cosmos_sdk/src/private_key/mod.rs index bb9bb32ae46..7e60ed1dec9 100644 --- a/rust/tw_cosmos_sdk/src/private_key/mod.rs +++ b/rust/tw_cosmos_sdk/src/private_key/mod.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_keypair::{tw, KeyPairError}; use tw_memory::Data; use tw_misc::traits::FromSlice; diff --git a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs index 8318bc676f5..655b80d5a51 100644 --- a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::private_key::CosmosPrivateKey; -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_keypair::tw; use tw_keypair::tw::Curve; use tw_keypair::KeyPairError; diff --git a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs index 93c0dc3b64e..88fb612fb00 100644 --- a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs +++ b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs @@ -6,7 +6,7 @@ use crate::context::CosmosContext; use crate::modules::compiler::tw_compiler::TWTransactionCompiler; use crate::modules::signer::tw_signer::TWSigner; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_proto::Common::Proto::SigningError; use tw_proto::Cosmos::Proto; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs index dd0e2df4957..32918948a7f 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs @@ -5,7 +5,7 @@ use crate::address::CosmosAddress; use crate::proto::cosmos; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::{google, to_any}; /// Supports Protobuf serialization only. diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs index b5fa6a99a4c..7b09ec52056 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs @@ -8,7 +8,7 @@ use crate::proto::cosmos; use crate::transaction::message::{message_to_json, CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; const DEFAULT_JSON_SEND_TYPE: &str = "cosmos-sdk/MsgSend"; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs index c6762b8ec07..584ea6e3a22 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs @@ -4,7 +4,7 @@ use crate::transaction::message::{CosmosMessage, JsonMessage}; use serde_json::Value as Json; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; /// Any raw JSON message. /// Supports JSON serialization only. diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs index 3f72c02bb35..6c990c14fff 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs @@ -5,7 +5,7 @@ use crate::address::CosmosAddress; use crate::proto::cosmos; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; pub enum VoteOption { diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs index 9c8d47d1281..dc3c6238b9c 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs @@ -8,7 +8,7 @@ use crate::proto::cosmos; use crate::transaction::message::{message_to_json, CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; const DEFAULT_JSON_SET_WITHDRAW_ADDRESS_TYPE: &str = "cosmos-sdk/MsgSetWithdrawAddress"; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs index 54eb8dca0b6..e3cb7ae29eb 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs @@ -7,7 +7,7 @@ use crate::modules::serializer::protobuf_serializer::build_coin; use crate::proto::ibc; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; use crate::transaction::Coin; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; pub struct Height { diff --git a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs index 3674b107903..0ecc466e1fc 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs @@ -5,7 +5,7 @@ use crate::modules::serializer::json_serializer::AnyMsg; use serde::Serialize; use serde_json::Value as Json; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::google; pub mod cosmos_auth_message; @@ -33,20 +33,23 @@ pub trait CosmosMessage { /// Override the method if the message can be represented as a Protobuf message. fn to_proto(&self) -> SigningResult { - Err(SigningError(SigningErrorType::Error_not_supported)) + SigningError::err(SigningErrorType::Error_not_supported) + .context("Message cannot be converted to Protobuf") } /// Override the method if the message can be represented as a JSON object. fn to_json(&self) -> SigningResult { - Err(SigningError(SigningErrorType::Error_not_supported)) + SigningError::err(SigningErrorType::Error_not_supported) + .context("Message cannot be converted to JSON") } } /// A standard implementation of the [`CosmosMessage::to_json`] method. /// This suits any message type that implements the `serialize` trait. pub fn message_to_json(msg_type: &str, msg: &T) -> SigningResult { - let value = - serde_json::to_value(msg).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let value = serde_json::to_value(msg) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Cosmos message to JSON")?; Ok(JsonMessage { msg_type: msg_type.to_string(), value, diff --git a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs index 0565add30ab..1ef8e86eaec 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs @@ -5,7 +5,7 @@ use crate::address::CosmosAddress; use crate::proto::stride; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_number::U256; use tw_proto::to_any; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs index 0613c22aa3f..b74e6a01bd5 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs @@ -10,7 +10,7 @@ use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; use serde_json::json; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; const DEFAULT_JSON_MSG_TYPE: &str = "wasm/MsgExecuteContract"; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs index 003adb077ec..6158c4f5815 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs @@ -6,7 +6,7 @@ use crate::modules::serializer::protobuf_serializer::build_coin; use crate::proto::types; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; use crate::transaction::Coin; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_number::U256; use tw_proto::to_any; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs index f647367d469..21163f691a3 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs @@ -9,7 +9,7 @@ use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; use serde_json::{json, Value as Json}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_number::U256; use tw_proto::to_any; @@ -38,7 +38,8 @@ impl ExecuteMsg { pub fn json(payload: Payload) -> SigningResult { let payload = serde_json::to_value(payload) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing message payload to JSON")?; Ok(ExecuteMsg::Json(payload)) } diff --git a/rust/tw_evm/src/abi/contract.rs b/rust/tw_evm/src/abi/contract.rs index 0226bb48744..f2ade089792 100644 --- a/rust/tw_evm/src/abi/contract.rs +++ b/rust/tw_evm/src/abi/contract.rs @@ -3,9 +3,10 @@ // Copyright © 2017 Trust Wallet. use crate::abi::function::Function; -use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use crate::abi::{AbiErrorKind, AbiResult}; use serde::{Deserialize, Deserializer}; use std::collections::BTreeMap; +use tw_coin_entry::error::prelude::*; /// API building calls to contracts ABI. /// Consider adding missing field such as `errors`, `events` etc. @@ -22,7 +23,8 @@ impl Contract { .into_iter() .flatten() .next() - .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch)) + .or_tw_err(AbiErrorKind::Error_abi_mismatch) + .with_context(|| format!("The given Smart Contract does not have '{name}' function")) } } diff --git a/rust/tw_evm/src/abi/decode.rs b/rust/tw_evm/src/abi/decode.rs index 0b13d192076..51165749d70 100644 --- a/rust/tw_evm/src/abi/decode.rs +++ b/rust/tw_evm/src/abi/decode.rs @@ -10,6 +10,7 @@ use crate::abi::token::Token; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use crate::address::Address; use lazy_static::lazy_static; +use tw_coin_entry::error::prelude::*; use tw_hash::{H160, H256}; use tw_number::{I256, U256}; @@ -22,8 +23,7 @@ lazy_static! { pub fn decode_params(params: &[Param], data: &[u8]) -> AbiResult> { let param_types: Vec<_> = params.iter().map(|param| param.kind.clone()).collect(); - let decoded_tokens = decode_params_impl(¶m_types, data) - .map_err(|_| AbiError(AbiErrorKind::Error_decoding_data))?; + let decoded_tokens = decode_params_impl(¶m_types, data)?; let named_tokens: Vec<_> = params .iter() @@ -65,7 +65,8 @@ fn decode_params_impl(types: &[ParamType], data: &[u8]) -> AbiResult> fn decode_offset(types: &[ParamType], data: &[u8]) -> AbiResult<(Vec, usize)> { // We don't support empty `FixedBytes` or `FixedArray` collections. if data.is_empty() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("Empty `FixedBytes` or `FixedArray` collections are not allowed"); } let mut tokens = vec![]; @@ -83,7 +84,8 @@ fn decode_offset(types: &[ParamType], data: &[u8]) -> AbiResult<(Vec, usi fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { match param { ParamType::Address => { - let slice = peek_32_bytes(data, offset)?; + let slice = peek_32_bytes(data, offset) + .with_context(|| format!("Error decoding Address parameter at {offset}"))?; let mut address = H160::default(); address.copy_from_slice(&slice[12..]); let result = DecodeResult { @@ -93,7 +95,8 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let slice = peek_32_bytes(data, offset)?; + let slice = peek_32_bytes(data, offset) + .with_context(|| format!("Error decoding Int parameter at {offset}"))?; let result = DecodeResult { token: Token::Int { int: I256::from_big_endian(slice), @@ -104,7 +107,8 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let slice = peek_32_bytes(data, offset)?; + let slice = peek_32_bytes(data, offset) + .with_context(|| format!("Error decoding Uint parameter at {offset}"))?; let result = DecodeResult { token: Token::Uint { uint: U256::from_big_endian(slice), @@ -115,7 +119,9 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let b = as_bool(&peek_32_bytes(data, offset)?)?; + let b = peek_32_bytes(data, offset) + .and_then(as_bool) + .with_context(|| format!("Error decoding Bool parameter at {offset}"))?; let result = DecodeResult { token: Token::Bool(b), new_offset: offset + WORD_LEN, @@ -125,8 +131,10 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { // FixedBytes is anything from bytes1 to bytes32. These values // are padded with trailing zeros to fill 32 bytes. - let bytes = take_bytes(data, offset, len.get())?; - let checked_bytes = NonEmptyBytes::new(bytes)?; + let bytes = take_bytes(data, offset, len.get()) + .with_context(|| format!("Error decoding FixedBytes parameter at {offset}"))?; + let checked_bytes = NonEmptyBytes::new(bytes) + .context("Empty `FixedBytes` collection is not allowed")?; let result = DecodeResult { token: Token::FixedBytes(checked_bytes), new_offset: offset + WORD_LEN, @@ -134,11 +142,17 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?; - let bytes_offset = add_checked(dynamic_offset, WORD_LEN)?; - - let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?; - let bytes = take_bytes(data, bytes_offset, len)?; + let dynamic_offset = peek_32_bytes(data, offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Bytes dynamic offset at {offset}"))?; + let bytes_offset = add_checked(dynamic_offset, WORD_LEN) + .with_context(|| format!("Dynamic offset is too big at {offset}"))?; + + let len = peek_32_bytes(data, dynamic_offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Bytes length at {dynamic_offset}"))?; + let bytes = take_bytes(data, bytes_offset, len) + .with_context(|| format!("Error decoding bytes at {bytes_offset}"))?; let result = DecodeResult { token: Token::Bytes(bytes), new_offset: offset + WORD_LEN, @@ -146,11 +160,17 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?; - let bytes_offset = add_checked(dynamic_offset, WORD_LEN)?; - - let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?; - let bytes = take_bytes(data, bytes_offset, len)?; + let dynamic_offset = peek_32_bytes(data, offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding String dynamic offset at {offset}"))?; + let bytes_offset = add_checked(dynamic_offset, WORD_LEN) + .with_context(|| format!("Dynamic offset is too big at {offset}"))?; + + let len = peek_32_bytes(data, dynamic_offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding String length at {dynamic_offset}"))?; + let bytes = take_bytes(data, bytes_offset, len) + .with_context(|| format!("Error decoding String parameter at {bytes_offset}"))?; let result = DecodeResult { // NOTE: We're decoding strings using lossy UTF-8 decoding to // prevent invalid strings written into contracts by either users or @@ -162,17 +182,23 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let len_offset = as_usize(&peek_32_bytes(data, offset)?)?; - let len = as_usize(&peek_32_bytes(data, len_offset)?)?; - - let tail_offset = add_checked(len_offset, WORD_LEN)?; + let len_offset = peek_32_bytes(data, offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Array length offset at {offset}"))?; + let len = peek_32_bytes(data, len_offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Array length at {len_offset}"))?; + + let tail_offset = add_checked(len_offset, WORD_LEN) + .with_context(|| format!("Array length offset is too big: {len_offset}"))?; let tail = &data[tail_offset..]; let mut tokens = vec![]; let mut new_offset = 0; - for _ in 0..len { - let res = decode_param(kind, tail, new_offset)?; + for elem_idx in 0..len { + let res = decode_param(kind, tail, new_offset) + .with_context(|| format!("Error decoding '{elem_idx}' Array element"))?; new_offset = res.new_offset; tokens.push(res.token); } @@ -191,9 +217,14 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult data.len() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("FixedArray dynamic offset is out of bounds"); } (&data[offset..], 0) } else { @@ -202,13 +233,15 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult AbiResult data.len() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("Tuple dynamic offset is out of bounds"); } (&data[offset..], 0) } else { @@ -239,8 +275,9 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult AbiResult AbiResult { +fn as_usize(slice: H256) -> AbiResult { if !slice[..28].iter().all(|x| *x == 0) { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data); } let result = ((slice[28] as usize) << 24) @@ -281,9 +318,9 @@ fn as_usize(slice: &H256) -> AbiResult { Ok(result) } -fn as_bool(slice: &H256) -> AbiResult { +fn as_bool(slice: H256) -> AbiResult { if !slice[..31].iter().all(|x| *x == 0) { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data); } Ok(slice[31] == 1) @@ -292,7 +329,7 @@ fn as_bool(slice: &H256) -> AbiResult { fn peek(data: &[u8], offset: usize, len: usize) -> AbiResult<&[u8]> { let end = add_checked(offset, len)?; if end > data.len() { - Err(AbiError(AbiErrorKind::Error_decoding_data)) + AbiError::err(AbiErrorKind::Error_decoding_data) } else { Ok(&data[offset..end]) } @@ -309,7 +346,7 @@ fn peek_32_bytes(data: &[u8], offset: usize) -> AbiResult { fn take_bytes(data: &[u8], offset: usize, len: usize) -> AbiResult> { let end = add_checked(offset, len)?; if end > data.len() { - Err(AbiError(AbiErrorKind::Error_decoding_data)) + AbiError::err(AbiErrorKind::Error_decoding_data) } else { Ok(data[offset..end].to_vec()) } @@ -317,7 +354,7 @@ fn take_bytes(data: &[u8], offset: usize, len: usize) -> AbiResult> { fn add_checked(left: usize, right: usize) -> AbiResult { left.checked_add(right) - .ok_or(AbiError(AbiErrorKind::Error_decoding_data)) + .or_tw_err(AbiErrorKind::Error_decoding_data) } #[cfg(test)] diff --git a/rust/tw_evm/src/abi/function.rs b/rust/tw_evm/src/abi/function.rs index 93a4d3d0441..8bb098625eb 100644 --- a/rust/tw_evm/src/abi/function.rs +++ b/rust/tw_evm/src/abi/function.rs @@ -11,6 +11,7 @@ use crate::abi::token::Token; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use itertools::Itertools; use serde::Deserialize; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; #[derive(Clone, Debug, Default, Deserialize)] @@ -52,9 +53,14 @@ impl Function { // Check if the given tokens match `Self::inputs` ABI. let input_param_types: Vec<_> = self.inputs.iter().map(|param| param.kind.clone()).collect(); - for (token, kind) in tokens.iter().zip(input_param_types.iter()) { - if token.to_param_type() != *kind { - return Err(AbiError(AbiErrorKind::Error_abi_mismatch)); + for (token_idx, (token, kind)) in tokens.iter().zip(input_param_types.iter()).enumerate() { + let actual_kind = token.to_param_type(); + if actual_kind != *kind { + return AbiError::err(AbiErrorKind::Error_abi_mismatch).with_context(|| { + format!( + "Expected {kind:?} type parameter at {token_idx}, found {actual_kind:?}" + ) + }); } } diff --git a/rust/tw_evm/src/abi/mod.rs b/rust/tw_evm/src/abi/mod.rs index 0be990ac556..dd59d3c6370 100644 --- a/rust/tw_evm/src/abi/mod.rs +++ b/rust/tw_evm/src/abi/mod.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; pub mod contract; pub mod decode; @@ -23,25 +23,21 @@ macro_rules! abi_output_error { let err = $error; let mut output = <$output>::default(); - output.error = err.0; - output.error_message = std::borrow::Cow::from(format!("{err:?}")); + output.error = *TWError::error_type(&err); + output.error_message = std::borrow::Cow::from(err.to_string()); output }}; } -pub type AbiResult = Result; pub type AbiErrorKind = tw_proto::EthereumAbi::Proto::AbiError; +pub type AbiError = TWError; +pub type AbiResult = Result; -#[derive(Debug)] -pub struct AbiError(pub AbiErrorKind); - -impl From for SigningError { - fn from(err: AbiError) -> Self { - match err.0 { - AbiErrorKind::OK => SigningError(SigningErrorType::OK), - AbiErrorKind::Error_internal => SigningError(SigningErrorType::Error_internal), - _ => SigningError(SigningErrorType::Error_invalid_params), - } - } +pub fn abi_to_signing_error(abi: AbiError) -> SigningError { + abi.map_err(|abi_kind| match abi_kind { + AbiErrorKind::OK => SigningErrorType::OK, + AbiErrorKind::Error_internal => SigningErrorType::Error_internal, + _ => SigningErrorType::Error_invalid_params, + }) } diff --git a/rust/tw_evm/src/abi/non_empty_array.rs b/rust/tw_evm/src/abi/non_empty_array.rs index 6bf1cd2f4e9..5b1c803dca5 100644 --- a/rust/tw_evm/src/abi/non_empty_array.rs +++ b/rust/tw_evm/src/abi/non_empty_array.rs @@ -6,6 +6,7 @@ use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use core::fmt; use std::num::NonZeroUsize; use std::ops::Deref; +use tw_coin_entry::error::prelude::*; pub type NonEmptyBytes = NonEmptyArray; @@ -16,7 +17,7 @@ pub struct NonZeroLen(NonZeroUsize); impl NonZeroLen { pub fn new(len: usize) -> AbiResult { NonZeroUsize::new(len) - .ok_or(AbiError(AbiErrorKind::Error_invalid_param_type)) + .or_tw_err(AbiErrorKind::Error_invalid_param_type) .map(NonZeroLen) } @@ -43,7 +44,7 @@ pub struct NonEmptyArray(Vec); impl NonEmptyArray { pub fn new(elements: Vec) -> AbiResult> { if elements.is_empty() { - return Err(AbiError(AbiErrorKind::Error_empty_type)); + return AbiError::err(AbiErrorKind::Error_empty_type); } Ok(NonEmptyArray(elements)) } diff --git a/rust/tw_evm/src/abi/param_type/constructor.rs b/rust/tw_evm/src/abi/param_type/constructor.rs index 25c25b8302b..d853cb9834c 100644 --- a/rust/tw_evm/src/abi/param_type/constructor.rs +++ b/rust/tw_evm/src/abi/param_type/constructor.rs @@ -6,6 +6,7 @@ use crate::abi::non_empty_array::NonZeroLen; use crate::abi::param_type::ParamType; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use tw_coin_entry::error::prelude::ResultContext; pub trait TypeConstructor: Sized { fn address() -> Self; @@ -99,7 +100,8 @@ impl TypeConstructor for ParamType { }) } - fn custom(_s: &str) -> AbiResult { - Err(AbiError(AbiErrorKind::Error_invalid_param_type)) + fn custom(s: &str) -> AbiResult { + AbiError::err(AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("`ParamType` doesn't support custom types like '{s}'")) } } diff --git a/rust/tw_evm/src/abi/param_type/reader.rs b/rust/tw_evm/src/abi/param_type/reader.rs index 826ebedf5c5..da7ee184428 100644 --- a/rust/tw_evm/src/abi/param_type/reader.rs +++ b/rust/tw_evm/src/abi/param_type/reader.rs @@ -7,6 +7,7 @@ use crate::abi::param_type::constructor::TypeConstructor; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use std::str::FromStr; +use tw_coin_entry::error::prelude::*; pub struct Reader; @@ -16,11 +17,15 @@ impl Reader { // Array if let Some(remaining) = s.strip_suffix(']') { let Some((element_type_str, len_str)) = remaining.rsplit_once('[') else { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("Invalid array type: {s}")); }; - let element_type = Reader::parse_type::(element_type_str)?; - if let Some(len) = parse_len(len_str)? { + let element_type = Reader::parse_type::(element_type_str) + .with_context(|| format!("Error parsing inner array type: {element_type_str}"))?; + if let Some(len) = parse_len(len_str) + .with_context(|| format!("Error parsing fixed_array length: {len_str}"))? + { return Ok(T::fixed_array_checked(len, element_type)); } return Ok(T::array(element_type)); @@ -28,28 +33,35 @@ impl Reader { let all_alphanumeric = s.chars().all(|ch| ch.is_ascii_alphanumeric()); if s.is_empty() || !all_alphanumeric { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("Expected an alpha-numeric string type: {s}")); } if s.contains(['[', ']']) { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type); } // uint, uint32, ... if let Some(len_str) = s.strip_prefix("uint") { - let bits = parse_uint_bits(len_str)?.unwrap_or_default(); + let bits = parse_uint_bits(len_str) + .with_context(|| format!("Error parsing uint bits: {len_str}"))? + .unwrap_or_default(); return Ok(T::uint_checked(bits)); } // int, int32, ... if let Some(len_str) = s.strip_prefix("int") { - let bits = parse_uint_bits(len_str)?.unwrap_or_default(); + let bits = parse_uint_bits(len_str) + .with_context(|| format!("Error parsing int bits: {len_str}"))? + .unwrap_or_default(); return Ok(T::int_checked(bits)); } // bytes, bytes32, ... if let Some(len_str) = s.strip_prefix("bytes") { - if let Some(len) = parse_len(len_str)? { + if let Some(len) = parse_len(len_str) + .with_context(|| format!("Error parsing fixed_bytes length: {len_str}"))? + { // Fixed-len bytes. return Ok(T::fixed_bytes_checked(len)); } @@ -93,9 +105,11 @@ fn parse_usize(usize_str: &str) -> AbiResult> { return Ok(None); } if usize_str.starts_with('0') { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type) + .context("Number cannot start with 0"); } usize::from_str(usize_str) .map(Some) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_param_type)) + .tw_err(|_| AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("Expected a decimal string: {usize_str}")) } diff --git a/rust/tw_evm/src/abi/uint.rs b/rust/tw_evm/src/abi/uint.rs index c2b090c274b..882b536b0a7 100644 --- a/rust/tw_evm/src/abi/uint.rs +++ b/rust/tw_evm/src/abi/uint.rs @@ -4,6 +4,7 @@ use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use std::fmt; +use tw_coin_entry::error::prelude::ResultContext; use tw_number::U256; #[derive(Clone, Copy, PartialEq)] @@ -47,7 +48,8 @@ impl From for usize { // https://docs.soliditylang.org/en/latest/abi-spec.html#types pub fn check_uint_bits(bits: usize) -> AbiResult<()> { if bits % 8 != 0 || bits == 0 || bits > 256 { - return Err(AbiError(AbiErrorKind::Error_invalid_uint_value)); + return AbiError::err(AbiErrorKind::Error_invalid_uint_value) + .with_context(|| format!("Unexpected Uint bits: {bits}")); } Ok(()) } diff --git a/rust/tw_evm/src/address.rs b/rust/tw_evm/src/address.rs index 6da55393fde..8cff9fb514d 100644 --- a/rust/tw_evm/src/address.rs +++ b/rust/tw_evm/src/address.rs @@ -8,7 +8,7 @@ use std::fmt::{Display, Formatter}; use std::ops::{RangeFrom, RangeInclusive}; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_hash::{sha3::keccak256, H160, H256}; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index ccb3f1bea91..0d26ad22b1b 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -8,11 +8,14 @@ use crate::abi::token::Token; use crate::address::Address; use crate::message::eip712::message_types::CustomTypes; use crate::message::eip712::property::PropertyType; -use crate::message::{EthMessage, MessageSigningError, MessageSigningResult}; +use crate::message::{ + EthMessage, MessageSigningError, MessageSigningErrorKind, MessageSigningResult, +}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; use std::str::FromStr; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{self, DecodeHex}; use tw_hash::sha3::keccak256; use tw_hash::{H160, H256}; @@ -38,11 +41,13 @@ impl Eip712Message { /// Tries to construct an EIP712 message from the given string. pub fn new>(message_to_sign: S) -> MessageSigningResult { let eip712_msg: Eip712Message = serde_json::from_str(message_to_sign.as_ref()) - .map_err(|_| MessageSigningError::TypeValueMismatch)?; + .tw_err(|_| MessageSigningErrorKind::TypeValueMismatch) + .context("Error deserializing EIP712 message as JSON")?; // Check if the given message is actually EIP712. if !eip712_msg.types.contains_key(EIP712_DOMAIN) { - return Err(MessageSigningError::TypeValueMismatch); + return MessageSigningError::err(MessageSigningErrorKind::TypeValueMismatch) + .context("EIP712 message does not contain domain info"); } Ok(eip712_msg) } @@ -56,9 +61,12 @@ impl Eip712Message { // Check if `domain.chainId` is expected. let chain_id_value = msg.domain["chainId"].clone(); let chain_id = U256::from_u64_or_decimal_str(chain_id_value) - .map_err(|_| MessageSigningError::TypeValueMismatch)?; + .tw_err(|_| MessageSigningErrorKind::TypeValueMismatch) + .context("Invalid chainId")?; if chain_id != expected_chain_id { - return Err(MessageSigningError::InvalidChainId); + return MessageSigningError::err(MessageSigningErrorKind::InvalidChainId).with_context( + || format!("Expected '{expected_chain_id}' chainId, found '{chain_id}'"), + ); } Ok(msg) @@ -71,12 +79,15 @@ impl EthMessage for Eip712Message { &self.types, PropertyType::Custom(EIP712_DOMAIN.to_string()), &self.domain, - )?; + ) + .context("Error encoding EIP712Domain")?; + let primary_data_hash = encode_data( &self.types, PropertyType::Custom(self.primary_type.clone()), &self.message, - )?; + ) + .context("Error encoding primary type")?; let concat = [ PREFIX.as_slice(), @@ -96,17 +107,22 @@ fn encode_data( data: &Json, ) -> MessageSigningResult> { match data_type { - PropertyType::Bool => encode_bool(data), - PropertyType::String => encode_string(data), - PropertyType::Int => encode_i256(data), - PropertyType::Uint => encode_u256(data), - PropertyType::Address => encode_address(data), - PropertyType::FixBytes { len } => encode_fix_bytes(data, len.get()), - PropertyType::Bytes => encode_bytes(data), - PropertyType::Custom(custom) => encode_custom(custom_types, &custom, data), - PropertyType::Array(element_type) => encode_array(custom_types, *element_type, data, None), + PropertyType::Bool => encode_bool(data).context("Error encoding 'bool' parameter"), + PropertyType::String => encode_string(data).context("Error encoding 'string' parameter"), + PropertyType::Int => encode_i256(data).context("Error encoding 'i256' parameter"), + PropertyType::Uint => encode_u256(data).context("Error encoding 'u256' parameter"), + PropertyType::Address => encode_address(data).context("Error encoding 'address' parameter"), + PropertyType::FixBytes { len } => { + encode_fix_bytes(data, len.get()).context("Error encoding 'bytes[N]' parameter") + }, + PropertyType::Bytes => encode_bytes(data).context("Error encoding 'bytes' parameter"), + PropertyType::Custom(custom) => encode_custom(custom_types, &custom, data) + .with_context(|| format!("Error encoding '{custom}' custom parameter")), + PropertyType::Array(element_type) => encode_array(custom_types, *element_type, data, None) + .context("Error encoding 'array' parameter"), PropertyType::FixArray { len, element_type } => { encode_array(custom_types, *element_type, data, Some(len.get())) + .context("Error encoding 'array[N]' parameter") }, } } @@ -114,14 +130,14 @@ fn encode_data( fn encode_bool(value: &Json) -> MessageSigningResult { let bin = value .as_bool() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; Ok(encode_tokens(&[Token::Bool(bin)])) } fn encode_string(value: &Json) -> MessageSigningResult { let string = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; let hash = keccak256(string.as_bytes()); let checked_bytes = NonEmptyBytes::new(hash).expect("`hash` must not be empty"); Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) @@ -129,23 +145,24 @@ fn encode_string(value: &Json) -> MessageSigningResult { fn encode_u256(value: &Json) -> MessageSigningResult { let uint = U256::from_u64_or_decimal_str(value.clone()) - .map_err(|_| MessageSigningError::InvalidParameterValue)?; + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; Ok(encode_tokens(&[Token::u256(uint)])) } fn encode_i256(value: &Json) -> MessageSigningResult { let int = I256::from_i64_or_decimal_str(value.clone()) - .map_err(|_| MessageSigningError::InvalidParameterValue)?; + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; Ok(encode_tokens(&[Token::i256(int)])) } fn encode_address(value: &Json) -> MessageSigningResult { let addr_str = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; // H160 doesn't require the string to be `0x` prefixed. let addr_data = - H160::from_str(addr_str).map_err(|_| MessageSigningError::InvalidParameterValue)?; + H160::from_str(addr_str).tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; + let addr = Address::from_bytes(addr_data); Ok(encode_tokens(&[Token::Address(addr)])) } @@ -153,24 +170,29 @@ fn encode_address(value: &Json) -> MessageSigningResult { fn encode_fix_bytes(value: &Json, expected_len: usize) -> MessageSigningResult { let str = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; let fix_bytes = - hex::decode_lenient(str).map_err(|_| MessageSigningError::InvalidParameterValue)?; - if fix_bytes.len() > expected_len { - return Err(MessageSigningError::TypeValueMismatch); + hex::decode_lenient(str).tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; + + let actual_len = fix_bytes.len(); + if actual_len > expected_len { + return MessageSigningError::err(MessageSigningErrorKind::TypeValueMismatch) + .with_context(|| format!("Expected '{expected_len}' bytes, found '{actual_len}'")); } - let checked_bytes = - NonEmptyBytes::new(fix_bytes).map_err(|_| MessageSigningError::InvalidParameterValue)?; + let checked_bytes = NonEmptyBytes::new(fix_bytes) + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue) + .context("Empty 'FixBytes' is not allowed")?; Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) } fn encode_bytes(value: &Json) -> MessageSigningResult { let str = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; let bytes = str .decode_hex() - .map_err(|_| MessageSigningError::InvalidParameterValue)?; + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; + let hash = keccak256(&bytes); let checked_bytes = NonEmptyBytes::new(hash).expect("`hash` must not be empty"); Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) @@ -184,16 +206,20 @@ fn encode_array( ) -> MessageSigningResult { let elements = data .as_array() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; // Check if the type definition actually matches the length of items to be encoded. - if expected_len.is_some() && Some(elements.len()) != expected_len { - return Err(MessageSigningError::TypeValueMismatch); + let actual_elements = elements.len(); + if expected_len.is_some() && Some(actual_elements) != expected_len { + return MessageSigningError::err(MessageSigningErrorKind::TypeValueMismatch).with_context( + || format!("Expected '{expected_len:?}' array elements, found '{actual_elements}'"), + ); } let mut encoded_items = vec![]; - for item in elements { - let mut encoded = encode_data(custom_types, element_type.clone(), item)?; + for (item_idx, item) in elements.iter().enumerate() { + let mut encoded = encode_data(custom_types, element_type.clone(), item) + .with_context(|| format!("Error encoding '{item_idx}' array element"))?; encoded_items.append(&mut encoded); } @@ -207,16 +233,18 @@ fn encode_custom( ) -> MessageSigningResult { let data_properties = custom_types .get(data_ident) - .ok_or(MessageSigningError::TypeValueMismatch)?; + .or_tw_err(MessageSigningErrorKind::TypeValueMismatch) + .with_context(|| format!("'{data_ident}' custom type is not specified"))?; let type_hash = encode_custom_type::type_hash(data_ident, custom_types)?; let checked_bytes = - NonEmptyBytes::new(type_hash).map_err(|_| MessageSigningError::InvalidParameterValue)?; + NonEmptyBytes::new(type_hash).tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; let mut encoded_tokens = encode_tokens(&[Token::FixedBytes(checked_bytes)]); - for field in data_properties.iter() { + for (field_idx, field) in data_properties.iter().enumerate() { let field_value = &data[&field.name]; - let field_property = PropertyType::from_str(&field.property_type)?; + let field_property = PropertyType::from_str(&field.property_type) + .with_context(|| format!("Error encoding '{field_idx}' field"))?; let mut encoded = encode_data(custom_types, field_property, field_value)?; encoded_tokens.append(&mut encoded); } @@ -242,7 +270,10 @@ mod encode_custom_type { ) -> MessageSigningResult { let deps = { let mut temp = build_dependencies(data_type, custom_types) - .ok_or(MessageSigningError::TypeValueMismatch)?; + .or_tw_err(MessageSigningErrorKind::TypeValueMismatch) + .with_context(|| { + format!("Error building '{data_type}' custom type dependencies") + })?; temp.remove(data_type); let mut temp = temp.into_iter().collect::>(); temp.sort_unstable(); diff --git a/rust/tw_evm/src/message/eip712/property.rs b/rust/tw_evm/src/message/eip712/property.rs index 3a85ed1c24c..fa9dfae8dae 100644 --- a/rust/tw_evm/src/message/eip712/property.rs +++ b/rust/tw_evm/src/message/eip712/property.rs @@ -7,10 +7,11 @@ use crate::abi::param_type::constructor::TypeConstructor; use crate::abi::param_type::reader::Reader; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; -use crate::message::MessageSigningError; +use crate::message::{MessageSigningError, MessageSigningErrorKind}; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; +use tw_coin_entry::error::prelude::*; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Property { @@ -79,7 +80,8 @@ impl TypeConstructor for PropertyType { } fn empty_tuple() -> AbiResult { - Err(AbiError(AbiErrorKind::Error_invalid_param_type)) + AbiError::err(AbiErrorKind::Error_invalid_param_type) + .context("`PropertyType` doesn't support tuples") } fn custom(s: &str) -> AbiResult { @@ -112,7 +114,7 @@ impl FromStr for PropertyType { type Err = MessageSigningError; fn from_str(s: &str) -> Result { - Reader::parse_type(s).map_err(|_| MessageSigningError::InvalidParameterType) + Reader::parse_type(s).tw_err(|_| MessageSigningErrorKind::InvalidParameterType) } } diff --git a/rust/tw_evm/src/message/mod.rs b/rust/tw_evm/src/message/mod.rs index f92a958e4a9..44b27d428ab 100644 --- a/rust/tw_evm/src/message/mod.rs +++ b/rust/tw_evm/src/message/mod.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; use tw_hash::H256; pub mod eip191; @@ -11,9 +11,10 @@ pub mod signature; pub type EthMessageBoxed = Box; pub type MessageSigningResult = Result; +pub type MessageSigningError = TWError; #[derive(Debug)] -pub enum MessageSigningError { +pub enum MessageSigningErrorKind { InvalidParameterType, InvalidParameterValue, TypeValueMismatch, @@ -21,17 +22,15 @@ pub enum MessageSigningError { Internal, } -impl From for SigningError { - fn from(err: MessageSigningError) -> Self { - match err { - MessageSigningError::InvalidParameterType - | MessageSigningError::InvalidParameterValue - | MessageSigningError::TypeValueMismatch - | MessageSigningError::InvalidChainId => { - SigningError(SigningErrorType::Error_invalid_params) - }, - MessageSigningError::Internal => SigningError(SigningErrorType::Error_internal), - } +pub fn to_signing(msg_err: MessageSigningError) -> SigningError { + match msg_err.error_type() { + MessageSigningErrorKind::InvalidParameterType + | MessageSigningErrorKind::InvalidParameterValue + | MessageSigningErrorKind::TypeValueMismatch + | MessageSigningErrorKind::InvalidChainId => { + SigningError::new(SigningErrorType::Error_invalid_params) + }, + MessageSigningErrorKind::Internal => SigningError::new(SigningErrorType::Error_internal), } } diff --git a/rust/tw_evm/src/modules/abi_encoder.rs b/rust/tw_evm/src/modules/abi_encoder.rs index de70367d618..4849bf6a735 100644 --- a/rust/tw_evm/src/modules/abi_encoder.rs +++ b/rust/tw_evm/src/modules/abi_encoder.rs @@ -26,6 +26,7 @@ use tw_proto::EthereumAbi::Proto; use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes, NonZeroLen}; use crate::abi::uint::UintBits; +use tw_coin_entry::error::prelude::*; use Proto::mod_ParamType::OneOfparam as ProtoParamType; use Proto::mod_ParamsDecodingInput::OneOfabi as AbiEnum; use Proto::mod_Token::OneOftoken as TokenEnum; @@ -76,7 +77,8 @@ impl AbiEncoder { input: Proto::ContractCallDecodingInput, ) -> AbiResult> { if input.encoded.len() < H32::len() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("Encoded Contract Call bytes too short"); } let short_signature = &input.encoded[0..H32::len()]; let short_signature = @@ -85,12 +87,18 @@ impl AbiEncoder { let mut abi_json: SmartContractCallAbiJson = serde_json::from_str(&input.smart_contract_abi_json) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_abi))?; + .tw_err(|_| AbiErrorKind::Error_invalid_abi) + .context("Error deserializing Smart Contract ABI as JSON")?; let function = abi_json .map .get_mut(&ContractCallSignature(short_signature)) - .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch))?; + .or_tw_err(AbiErrorKind::Error_abi_mismatch) + .with_context(|| { + format!( + "Contract Call ABI does not have a function with {short_signature} signature" + ) + })?; let decoded_tokens = function.decode_input(encoded_data)?; @@ -105,7 +113,8 @@ impl AbiEncoder { inputs: &decoded_tokens, }; let decoded_json = serde_json::to_string(&decoded_res) - .map_err(|_| AbiError(AbiErrorKind::Error_internal))?; + .tw_err(|_| AbiErrorKind::Error_internal) + .context("Error serializing Smart Contract Input as JSON")?; // Serialize the Proto parameters. let decoded_protos = decoded_tokens @@ -125,13 +134,16 @@ impl AbiEncoder { ) -> AbiResult> { let abi = match input.abi { AbiEnum::abi_json(abi_json) => serde_json::from_str(&abi_json) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_abi))?, + .tw_err(|_| AbiErrorKind::Error_invalid_abi) + .context("Error deserializing ABI as JSON")?, AbiEnum::abi_params(abi_params) => abi_params .params .into_iter() .map(Self::param_from_proto) .collect::>>()?, - AbiEnum::None => return Err(AbiError(AbiErrorKind::Error_invalid_abi)), + AbiEnum::None => { + return AbiError::err(AbiErrorKind::Error_invalid_abi).context("No ABI specified") + }, }; let decoded_tokens = decode_params(&abi, &input.encoded)?; @@ -150,7 +162,9 @@ impl AbiEncoder { fn decode_value_impl( input: Proto::ValueDecodingInput<'_>, ) -> AbiResult> { - let param_type = DecodingValueType::from_str(&input.param_type)?.0; + let param_type = DecodingValueType::from_str(&input.param_type) + .context("Invalid parameter type")? + .0; let token = decode_value(¶m_type, &input.encoded)?; let token_str = token.to_string(); Ok(Proto::ValueDecodingOutput { @@ -218,7 +232,8 @@ impl AbiEncoder { let proto_param_type = param .param - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type) + .context("Missing parameter type")?; let kind = Self::param_type_from_proto(proto_param_type)?; Ok(Param { @@ -250,12 +265,13 @@ impl AbiEncoder { TokenEnum::string_value(str) => Ok(Token::String(str.to_string())), TokenEnum::address(addr) => { let addr = Address::from_str(&addr) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_address_value))?; + .tw_err(|_| AbiErrorKind::Error_invalid_address_value)?; Ok(Token::Address(addr)) }, TokenEnum::byte_array(bytes) => Ok(Token::Bytes(bytes.to_vec())), TokenEnum::byte_array_fix(bytes) => { - let checked_bytes = NonEmptyBytes::new(bytes.to_vec())?; + let checked_bytes = NonEmptyBytes::new(bytes.to_vec()) + .context("Empty `FixedBytes` collection is not allowed")?; Ok(Token::FixedBytes(checked_bytes)) }, TokenEnum::array(arr) => { @@ -264,7 +280,8 @@ impl AbiEncoder { }, TokenEnum::fixed_array(arr) => { let (arr, kind) = Self::array_from_proto(arr)?; - let arr = NonEmptyArray::new(arr)?; + let arr = NonEmptyArray::new(arr) + .context("Empty `FixedArray` collection is not allowed")?; Ok(Token::FixedArray { arr, kind }) }, TokenEnum::tuple(Proto::TupleParam { params }) => { @@ -274,14 +291,14 @@ impl AbiEncoder { .collect::>>()?; Ok(Token::Tuple { params }) }, - TokenEnum::None => Err(AbiError(AbiErrorKind::Error_missing_param_value)), + TokenEnum::None => AbiError::err(AbiErrorKind::Error_missing_param_value), } } fn array_from_proto(array: Proto::ArrayParam<'_>) -> AbiResult<(Vec, ParamType)> { let element_type = array .element_type - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type)?; let element_type = Self::param_type_from_proto(element_type)?; let mut array_tokens = Vec::with_capacity(array.elements.len()); @@ -291,7 +308,9 @@ impl AbiEncoder { // Check if all tokens are the same as declared in `ArrayParam::element_type`. if token_type != element_type { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type).with_context(|| { + format!("Expected '{element_type:?}' array element type, found {token_type:?}") + }); } array_tokens.push(token); } @@ -347,13 +366,14 @@ impl AbiEncoder { ProtoParamType::address(_) => Ok(ParamType::Address), ProtoParamType::byte_array(_) => Ok(ParamType::Bytes), ProtoParamType::byte_array_fix(bytes) => { - let len = NonZeroLen::new(bytes.size as usize)?; + let len = NonZeroLen::new(bytes.size as usize) + .context("Expected non-zero 'FixByteArray' length")?; Ok(ParamType::FixedBytes { len }) }, ProtoParamType::array(arr) => { let element_type = arr .element_type - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type)?; let kind = Self::param_type_from_proto(*element_type)?; Ok(ParamType::Array { kind: Box::new(kind), @@ -362,7 +382,7 @@ impl AbiEncoder { ProtoParamType::fixed_array(arr) => { let element_type = arr .element_type - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type)?; let kind = Box::new(Self::param_type_from_proto(*element_type)?); let len = NonZeroLen::new(arr.size as usize)?; Ok(ParamType::FixedArray { kind, len }) @@ -374,11 +394,12 @@ impl AbiEncoder { .map(Self::param_from_proto) .collect::>>()?; if params.is_empty() { - return Err(AbiError(AbiErrorKind::Error_invalid_abi)); + return AbiError::err(AbiErrorKind::Error_invalid_abi) + .context("Empty 'Tuple' collection is not allowed"); } Ok(ParamType::Tuple { params }) }, - ProtoParamType::None => Err(AbiError(AbiErrorKind::Error_missing_param_type)), + ProtoParamType::None => AbiError::err(AbiErrorKind::Error_missing_param_type), } } @@ -413,13 +434,11 @@ impl AbiEncoder { } fn s_number_n_from_proto(encoded: &[u8]) -> AbiResult { - I256::from_big_endian_slice(encoded) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_uint_value)) + I256::from_big_endian_slice(encoded).tw_err(|_| AbiErrorKind::Error_invalid_uint_value) } fn u_number_n_from_proto(encoded: &[u8]) -> AbiResult { - U256::from_big_endian_slice(encoded) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_uint_value)) + U256::from_big_endian_slice(encoded).tw_err(|_| AbiErrorKind::Error_invalid_uint_value) } fn s_number_n_proto(i: I256, bits: UintBits) -> Proto::NumberNParam<'static> { @@ -474,7 +493,7 @@ impl FromStr for DecodingValueType { fn from_str(s: &str) -> Result { let param_type = ParamType::try_from_type_short(s)?; if param_type.has_tuple_components() { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type); } Ok(DecodingValueType(param_type)) } diff --git a/rust/tw_evm/src/modules/compiler.rs b/rust/tw_evm/src/modules/compiler.rs index cd961f6c66c..6dcac772299 100644 --- a/rust/tw_evm/src/modules/compiler.rs +++ b/rust/tw_evm/src/modules/compiler.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::marker::PhantomData; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ecdsa::secp256k1; use tw_number::U256; @@ -41,7 +41,9 @@ impl Compiler { fn preimage_hashes_impl( input: Proto::SigningInput<'_>, ) -> SigningResult> { - let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let chain_id = U256::from_big_endian_slice(&input.chain_id) + .into_tw() + .context("Invalid chain ID")?; let unsigned = TxBuilder::::tx_from_proto(&input)?; let prehash = unsigned.pre_hash(chain_id); @@ -65,7 +67,9 @@ impl Compiler { } = SingleSignaturePubkey::from_sign_list(signatures)?; let signature = secp256k1::Signature::from_bytes(&signature)?; - let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let chain_id = U256::from_big_endian_slice(&input.chain_id) + .into_tw() + .context("Invalid chain ID")?; let unsigned = TxBuilder::::tx_from_proto(&input)?; diff --git a/rust/tw_evm/src/modules/json_signer.rs b/rust/tw_evm/src/modules/json_signer.rs deleted file mode 100644 index d5a60a288bc..00000000000 --- a/rust/tw_evm/src/modules/json_signer.rs +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use crate::evm_context::EvmContext; -use std::marker::PhantomData; -use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_coin_entry::modules::json_signer::JsonSigner; -use tw_keypair::tw::PrivateKey; - -#[derive(Default)] -pub struct EthJsonSigner { - _phantom: PhantomData, -} - -impl JsonSigner for EthJsonSigner { - #[inline] - fn sign_json( - &self, - _coin: &dyn CoinContext, - _input_json: &str, - _key: &PrivateKey, - ) -> SigningResult { - // TODO implement when `quick_protobuf` is replaced with `rust-protobuf`. - Err(SigningError(SigningErrorType::Error_internal)) - } -} diff --git a/rust/tw_evm/src/modules/message_signer.rs b/rust/tw_evm/src/modules/message_signer.rs index 3b8b6c66e35..a67a60fb5ac 100644 --- a/rust/tw_evm/src/modules/message_signer.rs +++ b/rust/tw_evm/src/modules/message_signer.rs @@ -5,11 +5,11 @@ use crate::message::eip191::Eip191Message; use crate::message::eip712::eip712_message::Eip712Message; use crate::message::signature::{MessageSignature, SignatureType}; -use crate::message::{EthMessage, EthMessageBoxed}; +use crate::message::{to_signing, EthMessage, EthMessageBoxed}; use std::borrow::Cow; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::message_signer::MessageSigner; use tw_coin_entry::signing_output_error; use tw_encoding::hex::ToHex; @@ -61,7 +61,7 @@ impl EthMessageSigner { input: Proto::MessageSigningInput<'_>, ) -> SigningResult> { let msg = Self::message_from_proto(input)?; - let hash = msg.hash()?.to_vec(); + let hash = msg.hash().map_err(to_signing)?.to_vec(); Ok(CompilerProto::PreSigningOutput { data: Cow::Owned(hash.clone()), data_hash: Cow::Owned(hash), @@ -78,7 +78,7 @@ impl EthMessageSigner { let msg = Self::message_from_proto(input)?; - let hash_to_sign = msg.hash()?; + let hash_to_sign = msg.hash().map_err(to_signing)?; let secp_sign = private_key.sign(hash_to_sign)?; let prepared_sign = MessageSignature::prepared(secp_sign, signature_type)?; @@ -91,7 +91,9 @@ impl EthMessageSigner { fn verify_message_impl(input: Proto::MessageVerifyingInput<'_>) -> SigningResult { let public_key = secp256k1::PublicKey::try_from(input.public_key.as_ref())?; - let msg_hash = Self::message_from_str(&input.message)?.hash()?; + let msg_hash = Self::message_from_str(&input.message)? + .hash() + .map_err(to_signing)?; let secp_signature = MessageSignature::from_str(&input.signature)?.to_secp256k1_signature()?; @@ -113,9 +115,13 @@ impl EthMessageSigner { | Proto::MessageType::MessageType_typed_eip155 => match input.chain_id { Some(expected_chain_id) => { let expected_chain_id = U256::from(expected_chain_id.chain_id); - Ok(Eip712Message::new_checked(input.message, expected_chain_id)?.into_boxed()) + Ok(Eip712Message::new_checked(input.message, expected_chain_id) + .map_err(to_signing)? + .into_boxed()) }, - None => Ok(Eip712Message::new(input.message)?.into_boxed()), + None => Ok(Eip712Message::new(input.message) + .map_err(to_signing)? + .into_boxed()), }, } } diff --git a/rust/tw_evm/src/modules/mod.rs b/rust/tw_evm/src/modules/mod.rs index 6c23a104493..526e4bf78a1 100644 --- a/rust/tw_evm/src/modules/mod.rs +++ b/rust/tw_evm/src/modules/mod.rs @@ -4,7 +4,6 @@ pub mod abi_encoder; pub mod compiler; -pub mod json_signer; pub mod message_signer; pub mod rlp_encoder; pub mod signer; diff --git a/rust/tw_evm/src/modules/rlp_encoder.rs b/rust/tw_evm/src/modules/rlp_encoder.rs index 2530585e044..8f22880d811 100644 --- a/rust/tw_evm/src/modules/rlp_encoder.rs +++ b/rust/tw_evm/src/modules/rlp_encoder.rs @@ -9,7 +9,7 @@ use crate::rlp::RlpEncode; use std::borrow::Cow; use std::marker::PhantomData; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_memory::Data; use tw_number::U256; @@ -41,7 +41,8 @@ impl RlpEncoder { input: Proto::EncodingInput<'_>, ) -> SigningResult> { let Some(rlp_item) = input.item else { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No RLP item provided"); }; let initial_depth = 0; @@ -56,14 +57,18 @@ impl RlpEncoder { use Proto::mod_RlpItem::OneOfitem as Item; if depth >= RECURSION_LIMIT { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params).with_context(|| { + format!("Allowed complex types with the {RECURSION_LIMIT} maximum depth") + }); } let encoded_item = match rlp_item.item { Item::string_item(str) => RlpEncoder::::encode(str.as_ref()), Item::number_u64(num) => RlpEncoder::::encode(U256::from(num)), Item::number_u256(num_be) => { - let num = U256::from_big_endian_slice(num_be.as_ref())?; + let num = U256::from_big_endian_slice(num_be.as_ref()) + .into_tw() + .context("Invalid U256 number")?; RlpEncoder::::encode(num) }, Item::address(addr_s) => { @@ -83,7 +88,10 @@ impl RlpEncoder { }, // Pass the `raw_encoded` item as it is. Item::raw_encoded(encoded) => encoded.to_vec(), - Item::None => return Err(SigningError(SigningErrorType::Error_invalid_params)), + Item::None => { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No RLP item specified") + }, }; Ok(encoded_item) } diff --git a/rust/tw_evm/src/modules/signer.rs b/rust/tw_evm/src/modules/signer.rs index 8ca9b5e2b86..e136d2df1ec 100644 --- a/rust/tw_evm/src/modules/signer.rs +++ b/rust/tw_evm/src/modules/signer.rs @@ -6,7 +6,7 @@ use crate::evm_context::EvmContext; use crate::modules::tx_builder::TxBuilder; use std::borrow::Cow; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ecdsa::secp256k1; use tw_keypair::traits::SigningKeyTrait; @@ -29,7 +29,9 @@ impl Signer { fn sign_proto_impl( input: Proto::SigningInput<'_>, ) -> SigningResult> { - let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let chain_id = U256::from_big_endian_slice(&input.chain_id) + .into_tw() + .context("Invalid chain ID")?; let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; let unsigned = TxBuilder::::tx_from_proto(&input)?; diff --git a/rust/tw_evm/src/modules/tx_builder.rs b/rust/tw_evm/src/modules/tx_builder.rs index 5d23f9afa20..394fc6715c1 100644 --- a/rust/tw_evm/src/modules/tx_builder.rs +++ b/rust/tw_evm/src/modules/tx_builder.rs @@ -2,6 +2,7 @@ // // Copyright © 2017 Trust Wallet. +use crate::abi::abi_to_signing_error; use crate::abi::prebuild::erc1155::Erc1155; use crate::abi::prebuild::erc20::Erc20; use crate::abi::prebuild::erc4337::{Erc4337SimpleAccount, ExecuteArgs}; @@ -14,7 +15,7 @@ use crate::transaction::user_operation::UserOperation; use crate::transaction::UnsignedTransactionBox; use std::marker::PhantomData; use std::str::FromStr; -use tw_coin_entry::error::{AddressResult, SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_number::U256; use tw_proto::Common::Proto::SigningError as CommonError; @@ -32,63 +33,108 @@ impl TxBuilder { use Proto::TransactionMode as TxMode; let Some(ref transaction) = input.transaction else { - return Err(SigningError(CommonError::Error_invalid_params)); + return SigningError::err(CommonError::Error_invalid_params) + .context("No transaction specified"); }; let (eth_amount, payload, to) = match transaction.transaction_oneof { Tx::transfer(ref transfer) => { - let amount = U256::from_big_endian_slice(&transfer.amount)?; - let to_address = Self::parse_address(&input.to_address)?; + let amount = U256::from_big_endian_slice(&transfer.amount) + .into_tw() + .context("Invalid amount")?; + + let to_address = Self::parse_address(&input.to_address) + .context("Invalid destination address")?; + (amount, transfer.data.to_vec(), Some(to_address)) }, Tx::erc20_transfer(ref erc20_transfer) => { - let token_to_address = Self::parse_address(&erc20_transfer.to)?; - let token_amount = U256::from_big_endian_slice(&erc20_transfer.amount)?; - let contract_address = Self::parse_address(&input.to_address)?; + let token_to_address = Self::parse_address(&erc20_transfer.to) + .context("Invalid destination address")?; + + let token_amount = U256::from_big_endian_slice(&erc20_transfer.amount) + .into_tw() + .context("Invalid amount")?; + + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; - let payload = Erc20::transfer(token_to_address, token_amount)?; + let payload = Erc20::transfer(token_to_address, token_amount) + .map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::erc20_approve(ref erc20_approve) => { - let spender = Self::parse_address(&erc20_approve.spender)?; - let token_amount = U256::from_big_endian_slice(&erc20_approve.amount)?; - let contract_address = Self::parse_address(&input.to_address)?; + let spender = Self::parse_address(&erc20_approve.spender) + .context("Invalid sender address")?; - let payload = Erc20::approve(spender, token_amount)?; + let token_amount = U256::from_big_endian_slice(&erc20_approve.amount) + .into_tw() + .context("Invalid amount")?; + + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; + + let payload = + Erc20::approve(spender, token_amount).map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::erc721_transfer(ref erc721_transfer) => { - let from = Self::parse_address(&erc721_transfer.from)?; - let token_to_address = Self::parse_address(&erc721_transfer.to)?; - let token_id = U256::from_big_endian_slice(&erc721_transfer.token_id)?; - let contract_address = Self::parse_address(&input.to_address)?; + let from = + Self::parse_address(&erc721_transfer.from).context("Invalid sender address")?; + let token_to_address = Self::parse_address(&erc721_transfer.to) + .context("Invalid destination address")?; + + let token_id = U256::from_big_endian_slice(&erc721_transfer.token_id) + .into_tw() + .context("Invalid token ID")?; - let payload = Erc721::encode_transfer_from(from, token_to_address, token_id)?; + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; + + let payload = Erc721::encode_transfer_from(from, token_to_address, token_id) + .map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::erc1155_transfer(ref erc1155_transfer) => { - let from = Self::parse_address(&erc1155_transfer.from)?; - let to = Self::parse_address(&erc1155_transfer.to)?; - let token_id = U256::from_big_endian_slice(&erc1155_transfer.token_id)?; - let value = U256::from_big_endian_slice(&erc1155_transfer.value)?; + let from = Self::parse_address(&erc1155_transfer.from) + .context("Invalid sender address")?; + + let to = Self::parse_address(&erc1155_transfer.to) + .context("Invalid destination address")?; + + let token_id = U256::from_big_endian_slice(&erc1155_transfer.token_id) + .into_tw() + .context("Invalid token ID")?; + + let value = U256::from_big_endian_slice(&erc1155_transfer.value) + .into_tw() + .context("Invalid value")?; + let data = erc1155_transfer.data.to_vec(); - let contract_address = Self::parse_address(&input.to_address)?; + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; - let payload = Erc1155::encode_safe_transfer_from(from, to, token_id, value, data)?; + let payload = Erc1155::encode_safe_transfer_from(from, to, token_id, value, data) + .map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::contract_generic(ref contract_generic) => { - let amount = U256::from_big_endian_slice(&contract_generic.amount)?; + let amount = U256::from_big_endian_slice(&contract_generic.amount) + .into_tw() + .context("Invalid amount")?; + let payload = contract_generic.data.to_vec(); // `to_address` can be omitted for the generic contract call. // For example, on creating a new smart contract. - let to_address = Self::parse_address_optional(&input.to_address)?; + let to_address = Self::parse_address_optional(&input.to_address) + .context("Invalid destination address")?; (amount, payload, to_address) }, Tx::batch(ref batch) => { if input.tx_mode != TxMode::UserOp { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Transaction batch can be used in User Operation mode only"); } // Payload should match ERC4337 standard. @@ -97,12 +143,16 @@ impl TxBuilder { .iter() .map(Self::erc4337_execute_call_from_proto) .collect::, _>>()?; - let payload = Erc4337SimpleAccount::encode_execute_batch(calls)?; + let payload = Erc4337SimpleAccount::encode_execute_batch(calls) + .map_err(abi_to_signing_error)?; return Self::user_operation_from_proto(input, payload) .map(UserOperation::into_boxed); }, - Tx::None => return Err(SigningError(SigningErrorType::Error_invalid_params)), + Tx::None => { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction specified") + }, }; let tx = match input.tx_mode { @@ -113,13 +163,16 @@ impl TxBuilder { Self::transaction_eip1559_from_proto(input, eth_amount, payload, to)?.into_boxed() }, TxMode::UserOp => { - let to = to.ok_or(SigningError(SigningErrorType::Error_invalid_address))?; + let to = to + .or_tw_err(SigningErrorType::Error_invalid_address) + .context("No contract/destination address specified")?; // Payload should match the ERC4337 standard. let payload = Erc4337SimpleAccount::encode_execute(ExecuteArgs { to, value: eth_amount, data: payload, - })?; + }) + .map_err(abi_to_signing_error)?; Self::user_operation_from_proto(input, payload)?.into_boxed() }, @@ -131,8 +184,11 @@ impl TxBuilder { fn erc4337_execute_call_from_proto( call: &Proto::mod_Transaction::mod_Batch::BatchedCall, ) -> SigningResult { - let to = Self::parse_address(&call.address)?; - let value = U256::from_big_endian_slice(&call.amount)?; + let to = Self::parse_address(&call.address) + .context("Invalid 'BatchedCall' destination address")?; + let value = U256::from_big_endian_slice(&call.amount) + .into_tw() + .context("Invalid 'BatchedCall' amount")?; Ok(ExecuteArgs { to, value, @@ -147,9 +203,17 @@ impl TxBuilder { payload: Data, to_address: Option
, ) -> SigningResult { - let nonce = U256::from_big_endian_slice(&input.nonce)?; - let gas_price = U256::from_big_endian_slice(&input.gas_price)?; - let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let nonce = U256::from_big_endian_slice(&input.nonce) + .into_tw() + .context("Invalid nonce")?; + + let gas_price = U256::from_big_endian_slice(&input.gas_price) + .into_tw() + .context("Invalid gas price")?; + + let gas_limit = U256::from_big_endian_slice(&input.gas_limit) + .into_tw() + .context("Invalid gas limit")?; Ok(TransactionNonTyped { nonce, @@ -168,11 +232,22 @@ impl TxBuilder { payload: Data, to_address: Option
, ) -> SigningResult { - let nonce = U256::from_big_endian_slice(&input.nonce)?; - let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let nonce = U256::from_big_endian_slice(&input.nonce) + .into_tw() + .context("Invalid nonce")?; + + let gas_limit = U256::from_big_endian_slice(&input.gas_limit) + .into_tw() + .context("Invalid gas limit")?; + let max_inclusion_fee_per_gas = - U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas)?; - let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas)?; + U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas) + .into_tw() + .context("Invalid max inclusion fee per gas")?; + + let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas) + .into_tw() + .context("Invalid max fee per gas")?; Ok(TransactionEip1559 { nonce, @@ -190,19 +265,40 @@ impl TxBuilder { erc4337_payload: Data, ) -> SigningResult { let Some(ref user_op) = input.user_operation else { - return Err(SigningError(CommonError::Error_invalid_params)); + return SigningError::err(CommonError::Error_invalid_params) + .context("No user operation specified"); }; - let nonce = U256::from_big_endian_slice(&input.nonce)?; - let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let nonce = U256::from_big_endian_slice(&input.nonce) + .into_tw() + .context("Invalid nonce")?; + + let gas_limit = U256::from_big_endian_slice(&input.gas_limit) + .into_tw() + .context("Invalid gas limit")?; + let max_inclusion_fee_per_gas = - U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas)?; - let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas)?; + U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas) + .into_tw() + .context("Invalid max inclusion fee per gas")?; + + let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas) + .into_tw() + .context("Invalid max fee per gas")?; + + let entry_point = + Self::parse_address(user_op.entry_point.as_ref()).context("Invalid entry point")?; + + let sender = Self::parse_address(user_op.sender.as_ref()) + .context("Invalid User Operation sender")?; + + let verification_gas_limit = U256::from_big_endian_slice(&user_op.verification_gas_limit) + .into_tw() + .context("Invalid verification gas limit")?; - let entry_point = Self::parse_address(user_op.entry_point.as_ref())?; - let sender = Self::parse_address(user_op.sender.as_ref())?; - let verification_gas_limit = U256::from_big_endian_slice(&user_op.verification_gas_limit)?; - let pre_verification_gas = U256::from_big_endian_slice(&user_op.pre_verification_gas)?; + let pre_verification_gas = U256::from_big_endian_slice(&user_op.pre_verification_gas) + .into_tw() + .context("Invalid pre-verification gas")?; Ok(UserOperation { nonce, @@ -220,16 +316,18 @@ impl TxBuilder { } #[inline] - fn parse_address(addr: &str) -> AddressResult
{ - Context::Address::from_str(addr).map(Context::Address::into) + fn parse_address(addr: &str) -> SigningResult
{ + Context::Address::from_str(addr) + .map(Context::Address::into) + .map_err(SigningError::from) } #[inline] - fn parse_address_optional(addr: &str) -> AddressResult> { + fn parse_address_optional(addr: &str) -> SigningResult> { match Context::Address::from_str_optional(addr) { Ok(Some(addr)) => Ok(Some(addr.into())), Ok(None) => Ok(None), - Err(e) => Err(e), + Err(e) => Err(SigningError::from(e)), } } } diff --git a/rust/tw_evm/src/transaction/mod.rs b/rust/tw_evm/src/transaction/mod.rs index 04883063628..aa91320ce31 100644 --- a/rust/tw_evm/src/transaction/mod.rs +++ b/rust/tw_evm/src/transaction/mod.rs @@ -10,7 +10,7 @@ //! - User operations (EIP4337) use crate::transaction::signature::EthSignature; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_hash::{sha3::keccak256, H256}; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; diff --git a/rust/tw_evm/src/transaction/transaction_eip1559.rs b/rust/tw_evm/src/transaction/transaction_eip1559.rs index b86c28b2196..4d2c5130970 100644 --- a/rust/tw_evm/src/transaction/transaction_eip1559.rs +++ b/rust/tw_evm/src/transaction/transaction_eip1559.rs @@ -6,7 +6,7 @@ use crate::address::Address; use crate::rlp::list::RlpList; use crate::transaction::signature::{EthSignature, Signature}; use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; use tw_number::U256; diff --git a/rust/tw_evm/src/transaction/transaction_non_typed.rs b/rust/tw_evm/src/transaction/transaction_non_typed.rs index 7e2fd1194c9..9efd792020f 100644 --- a/rust/tw_evm/src/transaction/transaction_non_typed.rs +++ b/rust/tw_evm/src/transaction/transaction_non_typed.rs @@ -6,7 +6,7 @@ use crate::address::Address; use crate::rlp::list::RlpList; use crate::transaction::signature::{EthSignature, SignatureEip155}; use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; use tw_number::U256; diff --git a/rust/tw_evm/src/transaction/user_operation.rs b/rust/tw_evm/src/transaction/user_operation.rs index 0f3f23167ff..3f55658bb57 100644 --- a/rust/tw_evm/src/transaction/user_operation.rs +++ b/rust/tw_evm/src/transaction/user_operation.rs @@ -9,7 +9,7 @@ use crate::address::Address; use crate::transaction::signature::Signature; use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_hash::sha3::keccak256; use tw_hash::H256; diff --git a/rust/tw_evm/tests/barz.rs b/rust/tw_evm/tests/barz.rs index cea9732f5c5..e41a12aa244 100644 --- a/rust/tw_evm/tests/barz.rs +++ b/rust/tw_evm/tests/barz.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use std::borrow::Cow; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_evm::abi::prebuild::erc20::Erc20; use tw_evm::address::Address; diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index a00802a4c3d..e09efef30fc 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::message_signer::MessageSigner; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; diff --git a/rust/tw_evm/tests/rlp.rs b/rust/tw_evm/tests/rlp.rs index f25a831a0f5..490add080d1 100644 --- a/rust/tw_evm/tests/rlp.rs +++ b/rust/tw_evm/tests/rlp.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::str::FromStr; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_evm::evm_context::StandardEvmContext; use tw_evm::modules::rlp_encoder::{RlpEncoder, RECURSION_LIMIT}; diff --git a/rust/tw_evm/tests/signer.rs b/rust/tw_evm/tests/signer.rs index 33f50fa3080..eace3005b4c 100644 --- a/rust/tw_evm/tests/signer.rs +++ b/rust/tw_evm/tests/signer.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use std::borrow::Cow; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{self, ToHex}; use tw_evm::evm_context::StandardEvmContext; use tw_evm::modules::signer::Signer; diff --git a/rust/tw_proto/src/impls.rs b/rust/tw_proto/src/impls.rs new file mode 100644 index 00000000000..73c07a97ae0 --- /dev/null +++ b/rust/tw_proto/src/impls.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::Common::Proto::SigningError; +use crate::EthereumAbi::Proto::AbiError; +use std::fmt; + +impl fmt::Display for SigningError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err = match self { + SigningError::OK => "", + SigningError::Error_general => "Unknown error", + SigningError::Error_internal => "Internal error", + SigningError::Error_low_balance => "Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance", + SigningError::Error_zero_amount_requested => "Requested amount is zero, send of 0 makes no sense", + SigningError::Error_missing_private_key => "One required key is missing (too few or wrong keys are provided)", + SigningError::Error_invalid_private_key => "A private key provided is invalid (e.g. wrong size, usually should be 32 bytes)", + SigningError::Error_invalid_address => "A provided address (e.g. destination address) is invalid", + SigningError::Error_invalid_utxo => "A provided input UTXO is invalid", + SigningError::Error_invalid_utxo_amount => "The amount of an input UTXO is invalid", + SigningError::Error_wrong_fee => "Wrong fee is given, probably it is too low to cover minimal fee for the transaction", + SigningError::Error_signing => "General signing error", + SigningError::Error_tx_too_big => "Resulting transaction is too large", + SigningError::Error_missing_input_utxos => "No input UTXOs provided", + SigningError::Error_not_enough_utxos => "Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out)", + SigningError::Error_script_redeem => "Missing required redeem script", + SigningError::Error_script_output => "Invalid required output script", + SigningError::Error_script_witness_program => "Unrecognized witness program", + SigningError::Error_invalid_memo => "Invalid memo", + SigningError::Error_input_parse => "Some input field cannot be parsed", + SigningError::Error_no_support_n2n => "Multi-input and multi-output transaction not supported", + SigningError::Error_signatures_count => "Incorrect count of signatures passed to compile", + SigningError::Error_invalid_params => "Incorrect input parameter", + SigningError::Error_invalid_requested_token_amount => "Invalid input token amount", + SigningError::Error_not_supported => "Operation not supported for the chain", + SigningError::Error_dust_amount_requested => "Requested amount is too low (less dust)", + }; + write!(f, "{err}") + } +} + +impl fmt::Display for AbiError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err = match self { + AbiError::OK => "", + AbiError::Error_internal => "Internal error", + AbiError::Error_abi_mismatch => "ABI mismatch", + AbiError::Error_invalid_abi => "Invalid ABI provided", + AbiError::Error_invalid_param_type => "Invalid parameter type", + AbiError::Error_invalid_address_value => "Invalid address", + AbiError::Error_invalid_uint_value => "Invalid Uint", + AbiError::Error_missing_param_type => "Missing one of the parameter types", + AbiError::Error_missing_param_value => "Missing one of the parameter values", + AbiError::Error_decoding_data => "Error decoding data", + AbiError::Error_empty_type => "Empty array/tuple not allowed", + }; + write!(f, "{}", err) + } +} diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index aa7a3345b54..d5de0751f6b 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -7,6 +7,7 @@ use quick_protobuf::{BytesReader, MessageInfo, Writer}; #[allow(non_snake_case)] #[rustfmt::skip] mod common; +mod impls; #[allow(non_snake_case)] #[rustfmt::skip] diff --git a/rust/wallet_core_rs/tests/ethereum_rlp.rs b/rust/wallet_core_rs/tests/ethereum_rlp.rs index 48eec4bfa51..c5e4cf0ea63 100644 --- a/rust/wallet_core_rs/tests/ethereum_rlp.rs +++ b/rust/wallet_core_rs/tests/ethereum_rlp.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::ToHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/tools/install-sys-dependencies-linux b/tools/install-sys-dependencies-linux index b9173e25e42..eec6524cd06 100755 --- a/tools/install-sys-dependencies-linux +++ b/tools/install-sys-dependencies-linux @@ -2,5 +2,23 @@ set -e - # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake - sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev rustc --fix-missing +# Downgrade libc to temporarily fix https://github.com/actions/runner-images/issues/8659 +if [[ "$1" == "ci" ]]; then + LIBSTD_PACKAGE_VERSION="12.3.0-1ubuntu1~22.04" + # Bump this version if the CI has been broken due to the packages update. + LIBC_PACKAGE_VERSION="2.35-0ubuntu3.7" + + echo "Remove GCC 13 from runner image - runner-images/8659 workaround" + echo "NOTE: Bump $LIBC_PACKAGE_VERSION version if the CI has been broken due to the packages update" + + sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list + sudo apt-get update + sudo apt-get install -y --allow-downgrades \ + libc6=$LIBC_PACKAGE_VERSION \ + libc6-dev=$LIBC_PACKAGE_VERSION \ + libstdc++6=$LIBSTD_PACKAGE_VERSION \ + libgcc-s1=$LIBSTD_PACKAGE_VERSION +fi + +# build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake +sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev rustc --fix-missing