From 985f0ebba2151b5928ac3d11c8cbe2865303d0eb Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Mon, 9 Sep 2024 14:41:07 -0300 Subject: [PATCH 1/7] Implement `Amount` with proc macros Remove the UDL definition for Amount and `ParseAmountError`, replace it with proc macros --- src/bitcoin.udl | 22 ---------------------- src/error.rs | 2 +- src/lib.rs | 6 +++++- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/bitcoin.udl b/src/bitcoin.udl index cee7d09..b2cc539 100644 --- a/src/bitcoin.udl +++ b/src/bitcoin.udl @@ -10,18 +10,6 @@ interface Script { sequence to_bytes(); }; -interface Amount { - [Name=from_sat] - constructor(u64 from_sat); - - [Name=from_btc, Throws=ParseAmountError] - constructor(f64 from_btc); - - u64 to_sat(); - - f64 to_btc(); -}; - interface FeeRate { [Name=from_sat_per_vb, Throws=FeeRateError] constructor(u64 sat_per_vb); @@ -56,16 +44,6 @@ enum Network { // Errors // ------------------------------------------------------------------------ -[Error] -interface ParseAmountError { - OutOfRange(); - TooPrecise(); - MissingDigits(); - InputTooLarge(); - InvalidCharacter(string error_message); - OtherParseAmountErr(); -}; - [Error] interface FeeRateError { ArithmeticOverflow(); diff --git a/src/error.rs b/src/error.rs index 0d3b7ab..c7874a5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,7 +6,7 @@ pub enum FeeRateError { ArithmeticOverflow, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, uniffi::Error)] pub enum ParseAmountError { #[error("amount out of range")] OutOfRange, diff --git a/src/lib.rs b/src/lib.rs index 0a54e58..a4d731b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,13 +61,17 @@ impl Script { impl_from_core_type!(Script, BitcoinScriptBuf); impl_from_ffi_type!(Script, BitcoinScriptBuf); +#[derive(uniffi::Object)] pub struct Amount(pub BitcoinAmount); -impl Amount { +#[uniffi::export] +impl Amount { + #[uniffi::constructor(name = "from_sat")] pub fn from_sat(sat: u64) -> Self { Amount(BitcoinAmount::from_sat(sat)) } + #[uniffi::constructor] pub fn from_btc(btc: f64) -> Result { let bitcoin_amount = BitcoinAmount::from_btc(btc).map_err(ParseAmountError::from)?; Ok(Amount(bitcoin_amount)) From 59403164434455e815ee7161f1f8075e13914172 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Mon, 9 Sep 2024 14:46:08 -0300 Subject: [PATCH 2/7] Implement FeeRate with proc macros --- src/bitcoin.udl | 23 ----------------------- src/error.rs | 2 +- src/lib.rs | 5 ++++- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/bitcoin.udl b/src/bitcoin.udl index b2cc539..905e526 100644 --- a/src/bitcoin.udl +++ b/src/bitcoin.udl @@ -10,20 +10,6 @@ interface Script { sequence to_bytes(); }; -interface FeeRate { - [Name=from_sat_per_vb, Throws=FeeRateError] - constructor(u64 sat_per_vb); - - [Name=from_sat_per_kwu] - constructor(u64 sat_per_kwu); - - u64 to_sat_per_vb_ceil(); - - u64 to_sat_per_vb_floor(); - - u64 to_sat_per_kwu(); -}; - [Custom] typedef string Txid; @@ -39,12 +25,3 @@ enum Network { "Signet", "Regtest", }; - -// ------------------------------------------------------------------------ -// Errors -// ------------------------------------------------------------------------ - -[Error] -interface FeeRateError { - ArithmeticOverflow(); -}; diff --git a/src/error.rs b/src/error.rs index c7874a5..5ac662d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ use bitcoin::amount::ParseAmountError as BitcoinParseAmountError; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, uniffi::Error)] pub enum FeeRateError { #[error("arithmetic overflow on feerate")] ArithmeticOverflow, diff --git a/src/lib.rs b/src/lib.rs index a4d731b..9b2494b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,10 +12,12 @@ mod macros; pub mod error; pub use bitcoin::Network; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, uniffi::Object)] pub struct FeeRate(pub BitcoinFeeRate); +#[uniffi::export] impl FeeRate { + #[uniffi::constructor(name = "from_sat_per_vb")] pub fn from_sat_per_vb(sat_per_vb: u64) -> Result { let fee_rate: Option = BitcoinFeeRate::from_sat_per_vb(sat_per_vb); match fee_rate { @@ -24,6 +26,7 @@ impl FeeRate { } } + #[uniffi::constructor(name = "from_sat_per_kwu")] pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self { FeeRate(BitcoinFeeRate::from_sat_per_kwu(sat_per_kwu)) } From 3b0854e3b608e98d1db3ad82a6105e38ad31641e Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Mon, 9 Sep 2024 14:59:31 -0300 Subject: [PATCH 3/7] implement outpoint with proc macros --- src/bitcoin.udl | 8 -------- src/lib.rs | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/bitcoin.udl b/src/bitcoin.udl index 905e526..c5138e3 100644 --- a/src/bitcoin.udl +++ b/src/bitcoin.udl @@ -10,14 +10,6 @@ interface Script { sequence to_bytes(); }; -[Custom] -typedef string Txid; - -dictionary OutPoint { - Txid txid; - u32 vout; -}; - [NonExhaustive] enum Network { "Bitcoin", diff --git a/src/lib.rs b/src/lib.rs index 9b2494b..873dae8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ use bitcoin::Amount as BitcoinAmount; use bitcoin::FeeRate as BitcoinFeeRate; use bitcoin::ScriptBuf as BitcoinScriptBuf; -pub use bitcoin::OutPoint; -pub use bitcoin::Txid; +pub use bitcoin::OutPoint as BitcoinOutPoint; +pub use bitcoin::Txid as BitcoinTxid; use error::FeeRateError; use error::ParseAmountError; @@ -92,15 +92,27 @@ impl Amount { impl_from_core_type!(Amount, BitcoinAmount); impl_from_ffi_type!(Amount, BitcoinAmount); +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Txid(pub bitcoin::Txid); + +uniffi::custom_type!(Txid, String); + impl UniffiCustomTypeConverter for Txid { type Builtin = String; + fn into_custom(val: Self::Builtin) -> uniffi::Result { - Ok(val.parse::()?) + Ok(Txid(val.parse::().unwrap())) } fn from_custom(obj: Self) -> Self::Builtin { - obj.to_string() + obj.0.to_string() } } +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)] +pub struct OutPoint { + pub txid: Txid, + pub vout: u32, +} + uniffi::include_scaffolding!("bitcoin"); From 5033186bc65491b87bce73327dcf1d1b73807dd6 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Mon, 9 Sep 2024 15:04:21 -0300 Subject: [PATCH 4/7] implement Network with proc macros --- src/bitcoin.udl | 9 +-------- src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/bitcoin.udl b/src/bitcoin.udl index c5138e3..6591bb6 100644 --- a/src/bitcoin.udl +++ b/src/bitcoin.udl @@ -6,14 +6,7 @@ namespace bitcoin {}; interface Script { constructor(sequence raw_output_script); - + sequence to_bytes(); }; -[NonExhaustive] -enum Network { - "Bitcoin", - "Testnet", - "Signet", - "Regtest", -}; diff --git a/src/lib.rs b/src/lib.rs index 873dae8..86348e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,4 +115,36 @@ pub struct OutPoint { pub vout: u32, } +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Enum)] +#[non_exhaustive] +pub enum NetworkType { + Mainnet, + Testnet, + Signet, + Regtest, +} + +impl From for NetworkType { + fn from(network: Network) -> Self { + match network { + Network::Bitcoin => NetworkType::Mainnet, + Network::Testnet => NetworkType::Testnet, + Network::Signet => NetworkType::Signet, + Network::Regtest => NetworkType::Regtest, + _ => unreachable!(), + } + } +} + +impl From for Network { + fn from(network_type: NetworkType) -> Self { + match network_type { + NetworkType::Mainnet => Network::Bitcoin, + NetworkType::Testnet => Network::Testnet, + NetworkType::Signet => Network::Signet, + NetworkType::Regtest => Network::Regtest, + } + } +} + uniffi::include_scaffolding!("bitcoin"); From fde2e5c751943c341d56ab3a93272843adf85cb0 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 10 Sep 2024 11:53:07 -0300 Subject: [PATCH 5/7] implement script with proc macros This commit implements the last type with proc macros and removes the UDL file. --- build.rs | 3 --- src/bitcoin.udl | 12 ------------ src/lib.rs | 12 ++++++------ src/macros.rs | 3 ++- 4 files changed, 8 insertions(+), 22 deletions(-) delete mode 100644 build.rs delete mode 100644 src/bitcoin.udl diff --git a/build.rs b/build.rs deleted file mode 100644 index 2787def..0000000 --- a/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi::generate_scaffolding("./src/bitcoin.udl").unwrap(); -} diff --git a/src/bitcoin.udl b/src/bitcoin.udl deleted file mode 100644 index 6591bb6..0000000 --- a/src/bitcoin.udl +++ /dev/null @@ -1,12 +0,0 @@ -namespace bitcoin {}; - -// ------------------------------------------------------------------------ -// Core types -// ------------------------------------------------------------------------ - -interface Script { - constructor(sequence raw_output_script); - - sequence to_bytes(); -}; - diff --git a/src/lib.rs b/src/lib.rs index 86348e1..fd3f315 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ +uniffi::setup_scaffolding!(); + use bitcoin::Amount as BitcoinAmount; use bitcoin::FeeRate as BitcoinFeeRate; use bitcoin::ScriptBuf as BitcoinScriptBuf; -pub use bitcoin::OutPoint as BitcoinOutPoint; -pub use bitcoin::Txid as BitcoinTxid; use error::FeeRateError; use error::ParseAmountError; @@ -47,10 +47,12 @@ impl FeeRate { impl_from_core_type!(FeeRate, BitcoinFeeRate); impl_from_ffi_type!(FeeRate, BitcoinFeeRate); -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Object)] pub struct Script(pub BitcoinScriptBuf); +#[uniffi::export] impl Script { + #[uniffi::constructor] pub fn new(raw_output_script: Vec) -> Self { let script: BitcoinScriptBuf = raw_output_script.into(); Script(script) @@ -68,7 +70,7 @@ impl_from_ffi_type!(Script, BitcoinScriptBuf); pub struct Amount(pub BitcoinAmount); #[uniffi::export] -impl Amount { +impl Amount { #[uniffi::constructor(name = "from_sat")] pub fn from_sat(sat: u64) -> Self { Amount(BitcoinAmount::from_sat(sat)) @@ -146,5 +148,3 @@ impl From for Network { } } } - -uniffi::include_scaffolding!("bitcoin"); diff --git a/src/macros.rs b/src/macros.rs index 149b45b..3d0c6ae 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,4 +16,5 @@ macro_rules! impl_from_ffi_type { } } }; -} \ No newline at end of file +} + From f5c318f4e0d4a9fca09196d80a8bd790909bf281 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 10 Sep 2024 12:15:12 -0300 Subject: [PATCH 6/7] implement TxIn --- src/lib.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index fd3f315..ec452b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,10 @@ uniffi::setup_scaffolding!(); use bitcoin::Amount as BitcoinAmount; use bitcoin::FeeRate as BitcoinFeeRate; use bitcoin::ScriptBuf as BitcoinScriptBuf; +use bitcoin::TxIn as BitcoinTxIn; +use bitcoin::Weight as BitcoinWeight; +use bitcoin::Sequence as BitcoinSequence; +use bitcoin::Witness as BitcoinWitness; use error::FeeRateError; use error::ParseAmountError; @@ -117,6 +121,24 @@ pub struct OutPoint { pub vout: u32, } +impl From for OutPoint { + fn from(outpoint: bitcoin::OutPoint) -> Self { + OutPoint { + txid: Txid(outpoint.txid), + vout: outpoint.vout, + } + } +} + +impl From for bitcoin::OutPoint { + fn from(outpoint: OutPoint) -> Self { + bitcoin::OutPoint { + txid: outpoint.txid.0, + vout: outpoint.vout, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, uniffi::Enum)] #[non_exhaustive] pub enum NetworkType { @@ -148,3 +170,67 @@ impl From for Network { } } } + +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Object)] +pub struct Weight(pub BitcoinWeight); + +impl_from_core_type!(Weight, BitcoinWeight); +impl_from_ffi_type!(Weight, BitcoinWeight); + + +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Object)] +pub struct Sequence(pub BitcoinSequence); + +impl_from_core_type!(Sequence, BitcoinSequence); +impl_from_ffi_type!(Sequence, BitcoinSequence); + + +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Object)] +pub struct Witness(pub BitcoinWitness); + +impl_from_core_type!(Witness, BitcoinWitness); +impl_from_ffi_type!(Witness, BitcoinWitness); + + +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Object)] +pub struct TxIn(pub BitcoinTxIn); + +#[uniffi::export] +impl TxIn { + #[uniffi::constructor] + pub fn new(previous_output: OutPoint, sequence: u32, script_sig: Vec, witness: Vec>) -> Self { + TxIn(BitcoinTxIn { + previous_output: previous_output.into(), + sequence: bitcoin::Sequence(sequence), + script_sig: Script::new(script_sig).into(), + witness: BitcoinWitness::from_slice(&witness), + }) + } + + pub fn previous_output(&self) -> OutPoint { + self.0.previous_output.into() + } + + pub fn sequence(&self) -> Sequence { + self.0.sequence.into() + } + + pub fn script_sig(&self) -> Script { + self.0.script_sig.clone().into() + } + + pub fn witness(&self) -> Vec> { + self.0.witness.iter().map(|w| w.to_vec()).collect() + } + + pub fn weight(&self) -> Weight { + self.0.segwit_weight().into() + } + + pub fn total_size(&self) -> u32 { + self.0.total_size() as u32 + } +} + +impl_from_core_type!(TxIn, BitcoinTxIn); +impl_from_ffi_type!(TxIn, BitcoinTxIn); From ac755a9696ca972711dab9f8dbaea2d21ee1156a Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Tue, 10 Sep 2024 13:11:41 -0300 Subject: [PATCH 7/7] Implement transaction --- src/lib.rs | 226 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 188 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ec452b3..0b6669e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,9 @@ use bitcoin::TxIn as BitcoinTxIn; use bitcoin::Weight as BitcoinWeight; use bitcoin::Sequence as BitcoinSequence; use bitcoin::Witness as BitcoinWitness; +use bitcoin::Transaction as BitcoinTransaction; +use bitcoin::transaction::Version as BitcoinTxVersion; +use bitcoin::locktime::absolute::LockTime as BitcoinLockTime; use error::FeeRateError; use error::ParseAmountError; @@ -51,26 +54,26 @@ impl FeeRate { impl_from_core_type!(FeeRate, BitcoinFeeRate); impl_from_ffi_type!(FeeRate, BitcoinFeeRate); -#[derive(Clone, Debug, PartialEq, Eq, uniffi::Object)] -pub struct Script(pub BitcoinScriptBuf); +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)] +pub struct Script { + buffer: Vec, +} -#[uniffi::export] -impl Script { - #[uniffi::constructor] - pub fn new(raw_output_script: Vec) -> Self { - let script: BitcoinScriptBuf = raw_output_script.into(); - Script(script) +impl From for Script { + fn from(script: BitcoinScriptBuf) -> Self { + Script { + buffer: script.to_bytes(), + } } +} - pub fn to_bytes(&self) -> Vec { - self.0.to_bytes() +impl From