diff --git a/Cargo.lock b/Cargo.lock index ca011329..2614b6a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -634,6 +634,7 @@ dependencies = [ "serde_json", "strict_encoding", "strict_types", + "thiserror", "tokio", "toml 0.7.6", "tower-http", diff --git a/Cargo.toml b/Cargo.toml index f45c1815..7281e4e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ serde_json = "1.0.91" serde-encrypt = "0.7.0" strict_encoding = "~2.5" strict_types = "~1.5" +thiserror = "1.0" tokio = { version = "1.28.2", features = ["macros", "sync"] } zeroize = "1.6.0" diff --git a/src/bitcoin.rs b/src/bitcoin.rs index ca3be5b0..0ef89499 100644 --- a/src/bitcoin.rs +++ b/src/bitcoin.rs @@ -20,7 +20,7 @@ mod wallet; pub use crate::bitcoin::{ assets::dust_tx, - keys::{new_mnemonic, save_mnemonic}, + keys::{new_mnemonic, save_mnemonic, BitcoinKeysError}, payment::{create_payjoin, create_transaction}, psbt::{sign_psbt, sign_psbt_with_multiple_wallets}, wallet::{get_blockchain, get_wallet, sync_wallet, sync_wallets, MemoryWallet}, diff --git a/src/bitcoin/keys.rs b/src/bitcoin/keys.rs index 35532aa6..bf8ca1d2 100644 --- a/src/bitcoin/keys.rs +++ b/src/bitcoin/keys.rs @@ -1,18 +1,18 @@ use std::str::FromStr; -use anyhow::{anyhow, Result}; use bdk::{ bitcoin::{ secp256k1::Secp256k1, util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, KeySource}, }, keys::{DerivableKey, DescriptorKey, DescriptorKey::Secret as SecretDesc, DescriptorSecretKey}, - miniscript::Tap, + miniscript::{descriptor::DescriptorKeyParseError, Tap}, }; use bip39::{Language, Mnemonic}; use bitcoin_hashes::{sha256, Hash}; use miniscript_crate::DescriptorPublicKey; use nostr_sdk::prelude::{FromSkStr, ToBech32}; +use thiserror::Error; use zeroize::Zeroize; use crate::{ @@ -20,9 +20,38 @@ use crate::{ structs::{DecryptedWalletData, PrivateWalletData, PublicWalletData, SecretString}, }; -fn get_descriptor(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result { +#[derive(Error, Debug, Display)] +#[display(doc_comments)] +pub enum BitcoinKeysError { + /// Unexpected key variant in get_descriptor + UnexpectedKey, + /// Unexpected xpub descriptor + UnexpectedWatcherXpubDescriptor, + /// Unexpected key variant in nostr_keypair + UnexpectedKeyVariantInNostrKeypair, + /// BIP-32 error + Bip32Error(#[from] bitcoin::util::bip32::Error), + /// BIP-39 error + Bip39Error(#[from] bip39::Error), + /// BDK key error + BdkKeyError(#[from] bdk::keys::KeyError), + /// Miniscript descriptor key parse error + MiniscriptDescriptorKeyParseError(#[from] DescriptorKeyParseError), + /// getrandom error + GetRandomError(#[from] getrandom::Error), + /// Nostr SDK key error + NostrKeyError(#[from] nostr_sdk::key::Error), + /// Nostr SDK key error + NostrNip19Error(#[from] nostr_sdk::nips::nip19::Error), +} + +fn get_descriptor( + xprv: &ExtendedPrivKey, + path: &str, + change: u32, +) -> Result { let secp = Secp256k1::new(); - let deriv_descriptor: DerivationPath = DerivationPath::from_str(path)?; + let deriv_descriptor = DerivationPath::from_str(path)?; let derived_xprv = &xprv.derive_priv(&secp, &deriv_descriptor)?; let origin: KeySource = (xprv.fingerprint(&secp), deriv_descriptor); let derived_xprv_desc_key: DescriptorKey = derived_xprv.into_descriptor_key( @@ -33,17 +62,17 @@ fn get_descriptor(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result Result { +fn xprv_desc(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result { let xprv = get_descriptor(xprv, path, change)?; Ok(format!("tr({xprv})")) } -fn xpub_desc(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result { +fn xpub_desc(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result { let secp = Secp256k1::new(); let xprv = get_descriptor(xprv, path, change)?; let xpub = xprv.to_public(&secp)?; @@ -51,7 +80,11 @@ fn xpub_desc(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result Ok(format!("tr({xpub})")) } -fn watcher_xpub(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result { +fn watcher_xpub( + xprv: &ExtendedPrivKey, + path: &str, + change: u32, +) -> Result { let secp = Secp256k1::new(); let xprv = get_descriptor(xprv, path, change)?; let xpub = xprv.to_public(&secp)?; @@ -59,11 +92,15 @@ fn watcher_xpub(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result Result<(String, String)> { +fn nostr_keypair( + xprv: &ExtendedPrivKey, + path: &str, + change: u32, +) -> Result<(String, String), BitcoinKeysError> { let secp = Secp256k1::new(); let xprv = get_descriptor(xprv, path, change)?; @@ -78,11 +115,13 @@ fn nostr_keypair(xprv: &ExtendedPrivKey, path: &str, change: u32) -> Result<(Str hex::encode(first_keypair.x_only_public_key().0.serialize()), )) } else { - Err(anyhow!("Unexpected key variant in nostr_keypair")) + Err(BitcoinKeysError::UnexpectedKeyVariantInNostrKeypair) } } -pub async fn new_mnemonic(seed_password: &SecretString) -> Result { +pub async fn new_mnemonic( + seed_password: &SecretString, +) -> Result { let mut entropy = [0u8; 32]; getrandom::getrandom(&mut entropy)?; let mnemonic = Mnemonic::from_entropy_in(Language::English, &entropy)?; @@ -94,7 +133,7 @@ pub async fn new_mnemonic(seed_password: &SecretString) -> Result Result { +) -> Result { let mnemonic = Mnemonic::from_str(&mnemonic_phrase.0)?; get_mnemonic(mnemonic, seed_password).await @@ -103,7 +142,7 @@ pub async fn save_mnemonic( pub async fn get_mnemonic( mnemonic_phrase: Mnemonic, seed_password: &SecretString, -) -> Result { +) -> Result { let seed = mnemonic_phrase.to_seed_normalized(&seed_password.0); let network = NETWORK.read().await; diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..fbc4a52f --- /dev/null +++ b/src/error.rs @@ -0,0 +1,12 @@ +use thiserror::Error; + +use crate::{bitcoin::BitcoinKeysError, rgb::IssuerOperationError}; + +#[derive(Error, Debug, Display)] +#[display(doc_comments)] +pub enum BitMaskCoreError { + /// Bitcoin Keys Error + BitcoinKeysError(#[from] BitcoinKeysError), + /// RGB Issuer Operation Error + RgbIssuerOperationError(#[from] IssuerOperationError), +} diff --git a/src/lib.rs b/src/lib.rs index 57d98267..0ba3ea66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ extern crate amplify; pub mod bitcoin; pub mod carbonado; pub mod constants; +pub mod error; pub mod lightning; pub mod nostr; pub mod rgb; diff --git a/src/rgb.rs b/src/rgb.rs index c5b8d955..ef5e51e0 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -13,6 +13,7 @@ use rgbstd::{ persistence::{Stash, Stock}, }; use strict_encoding::StrictSerialize; +use thiserror::Error; pub mod accept; pub mod carbonado; @@ -73,7 +74,7 @@ use self::{ }, }; -#[derive(Debug, Clone, Eq, PartialEq, Display, From)] +#[derive(Debug, Clone, Eq, PartialEq, Display, From, Error)] #[display(doc_comments)] pub enum IssuerOperationError { // Some request data is missing. {0} @@ -91,7 +92,10 @@ pub enum IssuerOperationError { } /// RGB Operations -pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result { +pub async fn issue_contract( + sk: &str, + request: IssueRequest, +) -> Result { let IssueRequest { ticker, name, @@ -103,24 +107,19 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result stock, - _ => { - return Err(anyhow!(IssuerOperationError::Retrive( - CARBONADO_UNAVALIABLE.to_string(), - STOCK_UNAVALIABLE.to_string(), - ))) - } - }; - let mut rgb_account = match retrieve_wallets(sk, ASSETS_WALLETS).await { - Ok(rgb_account) => rgb_account, - _ => { - return Err(anyhow!(IssuerOperationError::Retrive( - CARBONADO_UNAVALIABLE.to_string(), - WALLET_UNAVALIABLE.to_string(), - ))) - } - }; + let mut stock = retrieve_stock(sk, ASSETS_STOCK).await.map_err(|_| { + IssuerOperationError::Retrive( + CARBONADO_UNAVALIABLE.to_string(), + STOCK_UNAVALIABLE.to_string(), + ) + })?; + + let mut rgb_account = retrieve_wallets(sk, ASSETS_WALLETS).await.map_err(|_| { + IssuerOperationError::Retrive( + CARBONADO_UNAVALIABLE.to_string(), + WALLET_UNAVALIABLE.to_string(), + ) + })?; let mut resolver = ExplorerResolver { explorer_url: BITCOIN_EXPLORER_API.read().await.to_string(), @@ -142,7 +141,7 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result None, }; - let contract = match create_contract( + let contract = create_contract( &ticker, &name, &description, @@ -154,10 +153,8 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result new_contract, - Err(err) => return Err(anyhow!(IssuerOperationError::Issue(err))), - }; + ) + .map_err(IssuerOperationError::Issue)?; let ContractResponse { contract_id, @@ -173,15 +170,13 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result contract, - Err(err) => return Err(anyhow!(IssuerOperationError::Export(err))), - }; + ) + .map_err(IssuerOperationError::Export)?; if let Some(wallet) = wallet { rgb_account @@ -190,19 +185,19 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result Result Result { +pub async fn create_watcher( + sk: &str, + request: WatcherRequest, +) -> Result { let WatcherRequest { name, xpub, force } = request; let mut rgb_account = retrieve_wallets(sk, ASSETS_WALLETS).await?; diff --git a/tests/rgb/integration/utils.rs b/tests/rgb/integration/utils.rs index 574ac433..a493dee3 100644 --- a/tests/rgb/integration/utils.rs +++ b/tests/rgb/integration/utils.rs @@ -5,6 +5,7 @@ use std::{collections::HashMap, env, process::Stdio}; use bdk::wallet::AddressIndex; use bitmask_core::{ bitcoin::{get_wallet, get_wallet_data, save_mnemonic, sync_wallet}, + error::BitMaskCoreError, rgb::{ create_invoice, create_psbt, create_watcher, import, issue_contract, transfer_asset, watcher_details, watcher_next_address, watcher_next_utxo, watcher_unspent_utxos, @@ -145,7 +146,7 @@ pub async fn issuer_issue_contract( force: bool, send_coins: bool, meta: Option, -) -> Result { +) -> Result { setup_regtest(force, None).await; let issuer_keys = save_mnemonic( &SecretString(ISSUER_MNEMONIC.to_string()),