diff --git a/tapyrus-wallet-ffi/Cargo.lock b/tapyrus-wallet-ffi/Cargo.lock index b205301..64d59b4 100644 --- a/tapyrus-wallet-ffi/Cargo.lock +++ b/tapyrus-wallet-ffi/Cargo.lock @@ -1631,9 +1631,10 @@ dependencies = [ [[package]] name = "tdk_chain" version = "0.15.0" -source = "git+https://github.com/chaintope/tdk?branch=fix_no_tpc_sent_and_no_tpc_change#9cfd7ed70ce2b16c195394c12cdd5d69f94b27de" +source = "git+https://github.com/chaintope/tdk?branch=for_test#889fdb4be1c88f5b18ad4714362e4c788753aa86" dependencies = [ "miniscript", + "num-bigint", "serde", "tapyrus", ] @@ -1641,7 +1642,7 @@ dependencies = [ [[package]] name = "tdk_esplora" version = "0.14.0" -source = "git+https://github.com/chaintope/tdk?branch=fix_no_tpc_sent_and_no_tpc_change#9cfd7ed70ce2b16c195394c12cdd5d69f94b27de" +source = "git+https://github.com/chaintope/tdk?branch=for_test#889fdb4be1c88f5b18ad4714362e4c788753aa86" dependencies = [ "async-trait", "esplora-client", @@ -1652,7 +1653,7 @@ dependencies = [ [[package]] name = "tdk_persist" version = "0.3.0" -source = "git+https://github.com/chaintope/tdk?branch=fix_no_tpc_sent_and_no_tpc_change#9cfd7ed70ce2b16c195394c12cdd5d69f94b27de" +source = "git+https://github.com/chaintope/tdk?branch=for_test#889fdb4be1c88f5b18ad4714362e4c788753aa86" dependencies = [ "anyhow", "tdk_chain", @@ -1661,7 +1662,7 @@ dependencies = [ [[package]] name = "tdk_sqlite" version = "0.1.0" -source = "git+https://github.com/chaintope/tdk?branch=fix_no_tpc_sent_and_no_tpc_change#9cfd7ed70ce2b16c195394c12cdd5d69f94b27de" +source = "git+https://github.com/chaintope/tdk?branch=for_test#889fdb4be1c88f5b18ad4714362e4c788753aa86" dependencies = [ "anyhow", "rusqlite", @@ -1674,7 +1675,7 @@ dependencies = [ [[package]] name = "tdk_wallet" version = "1.0.0-alpha.12" -source = "git+https://github.com/chaintope/tdk?branch=fix_no_tpc_sent_and_no_tpc_change#9cfd7ed70ce2b16c195394c12cdd5d69f94b27de" +source = "git+https://github.com/chaintope/tdk?branch=for_test#889fdb4be1c88f5b18ad4714362e4c788753aa86" dependencies = [ "anyhow", "getrandom", diff --git a/tapyrus-wallet-ffi/Cargo.toml b/tapyrus-wallet-ffi/Cargo.toml index 11326bf..5b9fd5c 100644 --- a/tapyrus-wallet-ffi/Cargo.toml +++ b/tapyrus-wallet-ffi/Cargo.toml @@ -20,9 +20,9 @@ default = ["uniffi/cli"] [dependencies] uniffi = { version = "=0.25.0" } -tdk_wallet = { git = "https://github.com/chaintope/tdk", branch = "fix_no_tpc_sent_and_no_tpc_change", subdirectory = "crates/wallet" } -tdk_sqlite = { git = "https://github.com/chaintope/tdk", branch = "fix_no_tpc_sent_and_no_tpc_change", subdirectory = "crates/sqlite" } -tdk_esplora = { git = "https://github.com/chaintope/tdk", branch = "fix_no_tpc_sent_and_no_tpc_change", subdirectory = "crates/esplora", features = ["blocking"] } +tdk_wallet = { git = "https://github.com/chaintope/tdk", branch = "for_test", subdirectory = "crates/wallet" } +tdk_sqlite = { git = "https://github.com/chaintope/tdk", branch = "for_test", subdirectory = "crates/sqlite" } +tdk_esplora = { git = "https://github.com/chaintope/tdk", branch = "for_test", subdirectory = "crates/esplora", features = ["blocking"] } [build-dependencies] uniffi = { version = "=0.25.0", features = ["build"] } diff --git a/tapyrus-wallet-ffi/src/lib.rs b/tapyrus-wallet-ffi/src/lib.rs index d220932..22191fd 100644 --- a/tapyrus-wallet-ffi/src/lib.rs +++ b/tapyrus-wallet-ffi/src/lib.rs @@ -104,6 +104,7 @@ pub(crate) struct TxOut { pub unspent: bool, } +#[derive(Debug, Clone)] pub(crate) struct Contract { pub contract_id: String, pub contract: String, @@ -111,6 +112,17 @@ pub(crate) struct Contract { pub payable: bool, } +impl From for Contract { + fn from(contract: tdk_wallet::chain::Contract) -> Self { + Contract { + contract_id: contract.contract_id, + contract: String::from_utf8(contract.contract).unwrap(), + payment_base: contract.payment_base.to_string(), + payable: contract.spendable, + } + } +} + pub(crate) struct GetNewAddressResult { pub address: String, pub public_key: String, @@ -310,6 +322,63 @@ impl Display for GetTxOutByAddressError { impl std::error::Error for GetTxOutByAddressError {} +#[derive(Debug)] +pub(crate) enum CalcPayToContractAddressError { + FailedToParsePublicKey, + InvalidColorId, + ContractError { cause: String }, +} + +impl Display for CalcPayToContractAddressError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + CalcPayToContractAddressError::FailedToParsePublicKey => { + write!(f, "Failed to parse public key") + } + CalcPayToContractAddressError::InvalidColorId => write!(f, "Invalid color id"), + CalcPayToContractAddressError::ContractError { cause: e } => { + write!(f, "Contract error: {}", e) + } + } + } +} + +impl std::error::Error for CalcPayToContractAddressError {} + +#[derive(Debug)] +pub(crate) enum StoreContractError { + ContractError { cause: String }, + FailedToParsePublicKey, +} + +impl Display for StoreContractError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + StoreContractError::ContractError { cause: e } => write!(f, "Contract error: {}", e), + StoreContractError::FailedToParsePublicKey => { + write!(f, "Failed to parse public key") + } + } + } +} + +impl std::error::Error for StoreContractError {} + +#[derive(Debug)] +pub(crate) enum UpdateContractError { + ContractError { cause: String }, +} + +impl Display for UpdateContractError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + UpdateContractError::ContractError { cause: e } => write!(f, "Contract error: {}", e), + } + } +} + +impl std::error::Error for UpdateContractError {} + impl HdWallet { pub fn new(config: Arc) -> Result { let Config { @@ -527,6 +596,9 @@ impl HdWallet { .unwrap(); TransferError::UnknownUtxo { utxo: utxo.clone() } } + AddUtxoError::ContractError => { + panic!("ContractError") + } })?; let mut psbt = @@ -627,22 +699,55 @@ impl HdWallet { public_key: String, contract: String, color_id: Option, - ) -> String { - "15Q1z9LJGeaU6oHeEvT1SKoeCUJntZZ9Tg".to_string() + ) -> Result { + let wallet = self.get_wallet(); + let payment_base = PublicKey::from_str(&public_key) + .map_err(|_| CalcPayToContractAddressError::FailedToParsePublicKey)?; + let contract = contract.as_bytes().to_vec(); + let color_id = match color_id { + Some(id) => Some( + ColorIdentifier::from_str(&id) + .map_err(|_| CalcPayToContractAddressError::InvalidColorId)?, + ), + None => None, + }; + let address = wallet + .create_pay_to_contract_address(&payment_base, contract, color_id) + .map_err(|e| CalcPayToContractAddressError::ContractError { + cause: e.to_string(), + })?; + Ok(address.to_string()) } - pub fn store_contract(&self, contract: Contract) -> () { - () + pub fn store_contract(&self, contract: Contract) -> Result { + let mut wallet = self.get_wallet(); + let payment_base = PublicKey::from_str(&contract.payment_base) + .map_err(|_| StoreContractError::FailedToParsePublicKey)?; + let contract = wallet + .store_contract( + contract.contract_id, + contract.contract.as_bytes().to_vec(), + payment_base, + contract.payable, + ) + .map_err(|e| StoreContractError::ContractError { + cause: e.to_string(), + })?; + Ok(contract.into()) } pub fn update_contract( &self, contract_id: String, - contract: Option, - payment_base: Option, - payable: Option, - ) -> () { - () + payable: bool, + ) -> Result<(), UpdateContractError> { + let mut wallet = self.get_wallet(); + wallet.update_contract(contract_id, payable).map_err(|e| { + UpdateContractError::ContractError { + cause: e.to_string(), + } + })?; + Ok(()) } } @@ -734,6 +839,43 @@ mod test { assert_eq!(balance, 0, "Balance should be 0"); } + #[test] + fn test_calc_p2c_address() { + let wallet = get_wallet(); + let public_key = + "039be0d2b0c3b6f7fad77f142257aee12b2a34047aa3191edc0424cd15e0fa15da".to_string(); + let address = wallet + .calc_p2c_address(public_key, "content".to_string(), None) + .expect("Failed to calculate P2C address"); + assert_eq!( + address, "1NUKT87AxtsJ74EiZ6esDz8kjppHS4cKz2", + "Address should be equal" + ); + } + + #[test] + fn test_store_contract() { + // remove sqlite file + let _ = fs::remove_file("tests/tapyrus-wallet.sqlite"); + + let wallet = get_wallet(); + let contract = Contract { + contract_id: "contract_id".to_string(), + contract: "contract".to_string(), + payment_base: "039be0d2b0c3b6f7fad77f142257aee12b2a34047aa3191edc0424cd15e0fa15da" + .to_string(), + payable: true, + }; + let stored_contract = wallet + .store_contract(contract.clone()) + .expect("Failed to store contract"); + + // Update contract + let updated_contract = wallet + .update_contract(contract.contract_id.clone(), false) + .expect("Failed to update contract"); + } + #[test] #[ignore] // This test is for manual testing with esplora-tapyrus. fn test_with_esplora() { @@ -817,6 +959,10 @@ mod test { .expect("Failed to transfer"); } + #[test] + #[ignore] // This test is for manual testing with esplora-tapyrus. + fn test_p2c_transfer() {} + #[test] #[ignore] // This test is for manual testing with esplora-tapyrus. fn test_get_transaction() { diff --git a/tapyrus-wallet-ffi/src/wallet.udl b/tapyrus-wallet-ffi/src/wallet.udl index 202b88a..fcdefc2 100644 --- a/tapyrus-wallet-ffi/src/wallet.udl +++ b/tapyrus-wallet-ffi/src/wallet.udl @@ -163,6 +163,33 @@ interface GetTxOutByAddressError { UnknownTransaction(); }; +/// The error for HDWallet#calc_p2c_address +[Error] +interface CalcPayToContractAddressError { + /// Occur if the public key is invalid + FailedToParsePublicKey(); + /// Occur if the contract is wrong + ContractError(string cause); + /// Occur if the color id is invalid + InvalidColorId(); +}; + +/// The error for HDWallet#store_contract +[Error] +interface StoreContractError { + /// Occur if the contract is wrong + ContractError(string cause); + /// Occur if the public key is invalid + FailedToParsePublicKey(); +}; + +/// The error for HDWallet#update_contract +[Error] +interface UpdateContractError { + /// Occur if the contract is wrong + ContractError(string cause); +}; + /// The HDWallet interface HdWallet { /// Create a new HDWallet instance @@ -198,10 +225,13 @@ interface HdWallet { sequence get_tx_out_by_address(string tx, string address); /// Get the pay to contract address + [Throws=CalcPayToContractAddressError] string calc_p2c_address(string public_key, string contract, string? color_id); /// Store the contract - void store_contract(Contract contract); + [Throws=StoreContractError] + Contract store_contract(Contract contract); /// Update the contract payable - void update_contract(string contract_id, string? contract, string? payment_base, boolean? payable); + [Throws=UpdateContractError] + void update_contract(string contract_id, boolean payable); };