Skip to content

Commit

Permalink
feat: add Transaction and PartiallySignedTransaction types
Browse files Browse the repository at this point in the history
  • Loading branch information
thunderbiscuit committed Oct 27, 2023
1 parent 1521811 commit 13c751c
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 335 deletions.
32 changes: 31 additions & 1 deletion bdk-ffi/src/bdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ interface TxBuilder {
TxBuilder fee_rate(float sat_per_vbyte);

[Throws=BdkError]
string finish([ByRef] Wallet wallet);
PartiallySignedTransaction finish([ByRef] Wallet wallet);
};

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -225,7 +225,37 @@ interface Address {

Network network();

Script script_pubkey();

string to_qr_uri();

string as_string();
};

interface Transaction {
[Throws=BdkError]
constructor(sequence<u8> transaction_bytes);

string txid();

u64 size();

u64 vsize();

boolean is_coin_base();

boolean is_explicitly_rbf();

boolean is_lock_time_enabled();

i32 version();
};

interface PartiallySignedTransaction {
[Throws=BdkError]
constructor(string psbt_base64);

string serialize();

Transaction extract_tx();
};
259 changes: 259 additions & 0 deletions bdk-ffi/src/bitcoin.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
use bdk::bitcoin::consensus::Decodable;
use bdk::bitcoin::network::constants::Network as BdkNetwork;
use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::bitcoin::Address as BdkAddress;
use bdk::bitcoin::Transaction as BdkTransaction;
use bdk::Error as BdkError;

use std::io::Cursor;
use std::str::FromStr;
use std::sync::{Arc, Mutex};

/// A Bitcoin script.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand All @@ -20,3 +31,251 @@ impl From<BdkScriptBuf> for Script {
Script(script)
}
}

pub enum Network {
/// Mainnet Bitcoin.
Bitcoin,
/// Bitcoin's testnet network.
Testnet,
/// Bitcoin's signet network.
Signet,
/// Bitcoin's regtest network.
Regtest,
}

impl From<Network> for BdkNetwork {
fn from(network: Network) -> Self {
match network {
Network::Bitcoin => BdkNetwork::Bitcoin,
Network::Testnet => BdkNetwork::Testnet,
Network::Signet => BdkNetwork::Signet,
Network::Regtest => BdkNetwork::Regtest,
}
}
}

impl From<BdkNetwork> for Network {
fn from(network: BdkNetwork) -> Self {
match network {
BdkNetwork::Bitcoin => Network::Bitcoin,
BdkNetwork::Testnet => Network::Testnet,
BdkNetwork::Signet => Network::Signet,
BdkNetwork::Regtest => Network::Regtest,
_ => panic!("Network {} not supported", network),
}
}
}

/// A Bitcoin address.
#[derive(Debug, PartialEq, Eq)]
pub struct Address {
inner: BdkAddress<NetworkChecked>,
}

impl Address {
pub fn new(address: String, network: Network) -> Result<Self, BdkError> {
Ok(Address {
inner: address
.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()
.unwrap() // TODO 11: Handle error correctly by rethrowing it as a BdkError
.require_network(network.into())
.map_err(|e| BdkError::Generic(e.to_string()))?,
})
}

/// alternative constructor
// fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
// BdkAddress::from_script(&script.inner, network)
// .map(|a| Address { inner: a })
// .map_err(|e| BdkError::Generic(e.to_string()))
// }
//
// fn payload(&self) -> Payload {
// match &self.inner.payload.clone() {
// BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
// pubkey_hash: pubkey_hash.to_vec(),
// },
// BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
// script_hash: script_hash.to_vec(),
// },
// BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
// version: *version,
// program: program.clone(),
// },
// }
// }

pub fn network(&self) -> Network {
self.inner.network.into()
}

pub fn script_pubkey(&self) -> Arc<Script> {
Arc::new(Script(self.inner.script_pubkey()))
}

pub fn to_qr_uri(&self) -> String {
self.inner.to_qr_uri()
}

pub fn as_string(&self) -> String {
self.inner.to_string()
}
}

impl From<Address> for BdkAddress {
fn from(address: Address) -> Self {
address.inner
}
}

impl From<BdkAddress> for Address {
fn from(address: BdkAddress) -> Self {
Address { inner: address }
}
}

/// A Bitcoin transaction.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transaction {
inner: BdkTransaction,
}

impl Transaction {
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
let mut decoder = Cursor::new(transaction_bytes);
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Transaction { inner: tx })
}

pub fn txid(&self) -> String {
self.inner.txid().to_string()
}

// fn weight(&self) -> u64 {
// self.inner.weight() as u64
// }

pub fn size(&self) -> u64 {
self.inner.size() as u64
}

pub fn vsize(&self) -> u64 {
self.inner.vsize() as u64
}

// fn serialize(&self) -> Vec<u8> {
// self.inner.serialize()
// }

pub fn is_coin_base(&self) -> bool {
self.inner.is_coin_base()
}

pub fn is_explicitly_rbf(&self) -> bool {
self.inner.is_explicitly_rbf()
}

pub fn is_lock_time_enabled(&self) -> bool {
self.inner.is_lock_time_enabled()
}

pub fn version(&self) -> i32 {
self.inner.version
}

// fn lock_time(&self) -> u32 {
// self.inner.lock_time.0
// }

// fn input(&self) -> Vec<TxIn> {
// self.inner.input.iter().map(|x| x.into()).collect()
// }
//
// fn output(&self) -> Vec<TxOut> {
// self.inner.output.iter().map(|x| x.into()).collect()
// }
}

impl From<BdkTransaction> for Transaction {
fn from(tx: BdkTransaction) -> Self {
Transaction { inner: tx }
}
}

pub(crate) struct PartiallySignedTransaction {
pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
}

impl PartiallySignedTransaction {
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
let psbt: BdkPartiallySignedTransaction =
BdkPartiallySignedTransaction::from_str(&psbt_base64)
.map_err(|e| BdkError::Generic(e.to_string()))?;

Ok(PartiallySignedTransaction {
inner: Mutex::new(psbt),
})
}

pub(crate) fn serialize(&self) -> String {
let psbt = self.inner.lock().unwrap().clone();
psbt.to_string()
}

// pub(crate) fn txid(&self) -> String {
// let tx = self.inner.lock().unwrap().clone().extract_tx();
// let txid = tx.txid();
// txid.to_hex()
// }

/// Return the transaction.
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
let tx = self.inner.lock().unwrap().clone().extract_tx();
Arc::new(tx.into())
}

// /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
// ///
// /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
// pub(crate) fn combine(
// &self,
// other: Arc<PartiallySignedTransaction>,
// ) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
// let other_psbt = other.inner.lock().unwrap().clone();
// let mut original_psbt = self.inner.lock().unwrap().clone();
//
// original_psbt.combine(other_psbt)?;
// Ok(Arc::new(PartiallySignedTransaction {
// inner: Mutex::new(original_psbt),
// }))
// }

// /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
// /// If the PSBT is missing a TxOut for an input returns None.
// pub(crate) fn fee_amount(&self) -> Option<u64> {
// self.inner.lock().unwrap().fee_amount()
// }

// /// The transaction's fee rate. This value will only be accurate if calculated AFTER the
// /// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
// /// transaction.
// /// If the PSBT is missing a TxOut for an input returns None.
// pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
// self.inner.lock().unwrap().fee_rate().map(Arc::new)
// }

// /// Serialize the PSBT data structure as a String of JSON.
// pub(crate) fn json_serialize(&self) -> String {
// let psbt = self.inner.lock().unwrap();
// serde_json::to_string(psbt.deref()).unwrap()
// }
}

impl From<BdkPartiallySignedTransaction> for PartiallySignedTransaction {
fn from(psbt: BdkPartiallySignedTransaction) -> Self {
PartiallySignedTransaction {
inner: Mutex::new(psbt),
}
}
}
9 changes: 5 additions & 4 deletions bdk-ffi/src/descriptor.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use crate::keys::DescriptorPublicKey;
use crate::keys::DescriptorSecretKey;
use crate::Network;

use bdk::bitcoin::bip32::Fingerprint;
use bdk::bitcoin::key::Secp256k1;
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
Expand All @@ -9,13 +13,10 @@ use bdk::template::{
};
use bdk::Error as BdkError;
use bdk::KeychainKind;

use std::str::FromStr;
use std::sync::Arc;

use crate::keys::DescriptorPublicKey;
use crate::keys::DescriptorSecretKey;
use crate::Network;

#[derive(Debug)]
pub struct Descriptor {
pub extended_descriptor: ExtendedDescriptor,
Expand Down
2 changes: 2 additions & 0 deletions bdk-ffi/src/esplora.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::wallet::{Update, Wallet};

use bdk::wallet::Update as BdkUpdate;
use bdk::Error as BdkError;
use bdk_esplora::esplora_client::{BlockingClient, Builder};
use bdk_esplora::EsploraExt;

use std::sync::Arc;

pub struct EsploraClient(BlockingClient);
Expand Down
9 changes: 5 additions & 4 deletions bdk-ffi/src/keys.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::Network;

use bdk::bitcoin::bip32::DerivationPath as BdkDerivationPath;
use bdk::bitcoin::key::Secp256k1;
use bdk::bitcoin::secp256k1::rand;
Expand All @@ -11,14 +13,13 @@ use bdk::keys::{
use bdk::miniscript::descriptor::{DescriptorXKey, Wildcard};
use bdk::miniscript::BareCtx;
use bdk::Error as BdkError;

use std::ops::Deref;
use std::str::FromStr;
use std::sync::{Arc, Mutex};

use crate::Network;

// /// Mnemonic phrases are a human-readable version of the private keys.
// /// Supported number of words are 12, 15, 18, 21 and 24.
/// Mnemonic phrases are a human-readable version of the private keys.
/// Supported number of words are 12, 15, 18, 21 and 24.
pub(crate) struct Mnemonic {
inner: BdkMnemonic,
}
Expand Down
Loading

0 comments on commit 13c751c

Please sign in to comment.