Compare commits
15 Commits
release/0.
...
v0.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0648075555 | ||
|
|
3f81346e6b | ||
|
|
a25fb1348d | ||
|
|
ae1ea99ed3 | ||
|
|
9a381f6d32 | ||
|
|
3a07b4838a | ||
|
|
29de6f2d06 | ||
|
|
73ba73fd03 | ||
|
|
9866649fdc | ||
|
|
c2aecb0597 | ||
|
|
ba71a7a27c | ||
|
|
036e790a75 | ||
|
|
ee6ee8139a | ||
|
|
3bec5d2cab | ||
|
|
b9aa0a2cf1 |
2
.github/workflows/cont_integration.yml
vendored
2
.github/workflows/cont_integration.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
|||||||
if: ${{ matrix.rust.clippy }}
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: cargo clippy --all-targets -- -D warnings
|
run: cargo clippy --all-targets -- -D warnings
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test
|
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Rust fmt
|
name: Rust fmt
|
||||||
|
|||||||
20
Cargo.toml
20
Cargo.toml
@@ -1,23 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bdk-ffi"
|
name = "bdk-ffi"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [".","bdk-ffi-bindgen"]
|
members = [".","bdk-ffi-bindgen"]
|
||||||
default-members = [".", "bdk-ffi-bindgen"]
|
default-members = [".", "bdk-ffi-bindgen"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["staticlib", "cdylib"]
|
crate-type = ["staticlib", "cdylib"]
|
||||||
name = "bdkffi"
|
name = "bdkffi"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdk = { version = "0.23", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
bdk = { version = "0.24", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
||||||
|
|
||||||
uniffi_macros = { version = "0.20.0", features = ["builtin-bindgen"] }
|
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
uniffi = { version = "0.20.0", features = ["builtin-bindgen"] }
|
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
uniffi_build = { version = "0.20.0", features = ["builtin-bindgen"] }
|
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
|
|
||||||
|
[profile.release-smaller]
|
||||||
|
inherits = "release"
|
||||||
|
opt-level = 'z' # Optimize for size.
|
||||||
|
lto = true # Enable Link Time Optimization
|
||||||
|
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||||
|
panic = 'abort' # Abort on panic
|
||||||
|
strip = true # Strip symbols from binary*
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.45" # remove after upgrading to next version of uniffi
|
anyhow = "1.0.45" # remove after upgrading to next version of uniffi
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
uniffi_bindgen = "0.20.0"
|
uniffi_bindgen = "0.21.0"
|
||||||
camino = "1.0.9"
|
camino = "1.0.9"
|
||||||
|
|||||||
43
src/bdk.udl
43
src/bdk.udl
@@ -1,6 +1,5 @@
|
|||||||
namespace bdk {
|
namespace bdk {
|
||||||
[Throws=BdkError]
|
|
||||||
string generate_mnemonic(WordCount word_count);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
@@ -33,9 +32,9 @@ enum BdkError {
|
|||||||
"ProgressUpdateError",
|
"ProgressUpdateError",
|
||||||
"InvalidOutpoint",
|
"InvalidOutpoint",
|
||||||
"Descriptor",
|
"Descriptor",
|
||||||
"AddressValidator",
|
|
||||||
"Encode",
|
"Encode",
|
||||||
"Miniscript",
|
"Miniscript",
|
||||||
|
"MiniscriptPsbt",
|
||||||
"Bip32",
|
"Bip32",
|
||||||
"Secp256k1",
|
"Secp256k1",
|
||||||
"Json",
|
"Json",
|
||||||
@@ -138,7 +137,7 @@ interface Blockchain {
|
|||||||
constructor(BlockchainConfig config);
|
constructor(BlockchainConfig config);
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
void broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
|
void broadcast([ByRef] PartiallySignedTransaction psbt);
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
u32 get_height();
|
u32 get_height();
|
||||||
@@ -189,7 +188,7 @@ interface Wallet {
|
|||||||
Balance get_balance();
|
Balance get_balance();
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
boolean sign([ByRef] PartiallySignedBitcoinTransaction psbt);
|
boolean sign([ByRef] PartiallySignedTransaction psbt);
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
sequence<TransactionDetails> list_transactions();
|
sequence<TransactionDetails> list_transactions();
|
||||||
@@ -203,7 +202,14 @@ interface Wallet {
|
|||||||
sequence<LocalUtxo> list_unspent();
|
sequence<LocalUtxo> list_unspent();
|
||||||
};
|
};
|
||||||
|
|
||||||
interface PartiallySignedBitcoinTransaction {
|
interface FeeRate {
|
||||||
|
[Name=from_sat_per_vb]
|
||||||
|
constructor(float sat_per_vb);
|
||||||
|
|
||||||
|
float as_sat_per_vb();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PartiallySignedTransaction {
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
constructor(string psbt_base64);
|
constructor(string psbt_base64);
|
||||||
|
|
||||||
@@ -214,11 +220,15 @@ interface PartiallySignedBitcoinTransaction {
|
|||||||
sequence<u8> extract_tx();
|
sequence<u8> extract_tx();
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
PartiallySignedBitcoinTransaction combine(PartiallySignedBitcoinTransaction other);
|
PartiallySignedTransaction combine(PartiallySignedTransaction other);
|
||||||
|
|
||||||
|
u64? fee_amount();
|
||||||
|
|
||||||
|
FeeRate? fee_rate();
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary TxBuilderResult {
|
dictionary TxBuilderResult {
|
||||||
PartiallySignedBitcoinTransaction psbt;
|
PartiallySignedTransaction psbt;
|
||||||
TransactionDetails transaction_details;
|
TransactionDetails transaction_details;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -271,7 +281,19 @@ interface BumpFeeTxBuilder {
|
|||||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet);
|
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Mnemonic {
|
||||||
|
constructor(WordCount word_count);
|
||||||
|
|
||||||
|
[Name=from_string, Throws=BdkError]
|
||||||
|
constructor(string mnemonic);
|
||||||
|
|
||||||
|
[Name=from_entropy, Throws=BdkError]
|
||||||
|
constructor(sequence<u8> entropy);
|
||||||
|
|
||||||
|
string as_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DerivationPath {
|
interface DerivationPath {
|
||||||
@@ -280,8 +302,7 @@ interface DerivationPath {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface DescriptorSecretKey {
|
interface DescriptorSecretKey {
|
||||||
[Throws=BdkError]
|
constructor(Network network, Mnemonic mnemonic, string? password);
|
||||||
constructor(Network network, string mnemonic, string? password);
|
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
DescriptorSecretKey derive(DerivationPath path);
|
DescriptorSecretKey derive(DerivationPath path);
|
||||||
|
|||||||
147
src/lib.rs
147
src/lib.rs
@@ -3,7 +3,8 @@ use bdk::bitcoin::hashes::hex::ToHex;
|
|||||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||||
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
||||||
use bdk::bitcoin::util::psbt::serialize::Serialize;
|
use bdk::bitcoin::util::psbt::serialize::Serialize;
|
||||||
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
|
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||||
|
use bdk::bitcoin::Sequence;
|
||||||
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
|
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
|
||||||
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
|
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
|
||||||
use bdk::blockchain::GetBlockHash;
|
use bdk::blockchain::GetBlockHash;
|
||||||
@@ -15,12 +16,13 @@ use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress};
|
|||||||
use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration};
|
use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration};
|
||||||
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
|
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
|
||||||
use bdk::descriptor::DescriptorXKey;
|
use bdk::descriptor::DescriptorXKey;
|
||||||
use bdk::keys::bip39::{Language, Mnemonic, WordCount};
|
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
|
||||||
use bdk::keys::{
|
use bdk::keys::{
|
||||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||||
};
|
};
|
||||||
use bdk::miniscript::BareCtx;
|
use bdk::miniscript::BareCtx;
|
||||||
|
use bdk::psbt::PsbtUtils;
|
||||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||||
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
||||||
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
||||||
@@ -209,7 +211,7 @@ impl Blockchain {
|
|||||||
self.blockchain_mutex.lock().expect("blockchain")
|
self.blockchain_mutex.lock().expect("blockchain")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn broadcast(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<(), BdkError> {
|
fn broadcast(&self, psbt: &PartiallySignedTransaction) -> Result<(), BdkError> {
|
||||||
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
|
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
|
||||||
self.get_blockchain().broadcast(&tx)
|
self.get_blockchain().broadcast(&tx)
|
||||||
}
|
}
|
||||||
@@ -340,14 +342,15 @@ impl fmt::Debug for ProgressHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PartiallySignedBitcoinTransaction {
|
pub struct PartiallySignedTransaction {
|
||||||
internal: Mutex<PartiallySignedTransaction>,
|
internal: Mutex<BdkPartiallySignedTransaction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartiallySignedBitcoinTransaction {
|
impl PartiallySignedTransaction {
|
||||||
fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
||||||
let psbt: PartiallySignedTransaction = PartiallySignedTransaction::from_str(&psbt_base64)?;
|
let psbt: BdkPartiallySignedTransaction =
|
||||||
Ok(PartiallySignedBitcoinTransaction {
|
BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
|
||||||
|
Ok(PartiallySignedTransaction {
|
||||||
internal: Mutex::new(psbt),
|
internal: Mutex::new(psbt),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -378,16 +381,30 @@ impl PartiallySignedBitcoinTransaction {
|
|||||||
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||||
fn combine(
|
fn combine(
|
||||||
&self,
|
&self,
|
||||||
other: Arc<PartiallySignedBitcoinTransaction>,
|
other: Arc<PartiallySignedTransaction>,
|
||||||
) -> Result<Arc<PartiallySignedBitcoinTransaction>, BdkError> {
|
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||||
let other_psbt = other.internal.lock().unwrap().clone();
|
let other_psbt = other.internal.lock().unwrap().clone();
|
||||||
let mut original_psbt = self.internal.lock().unwrap().clone();
|
let mut original_psbt = self.internal.lock().unwrap().clone();
|
||||||
|
|
||||||
original_psbt.combine(other_psbt)?;
|
original_psbt.combine(other_psbt)?;
|
||||||
Ok(Arc::new(PartiallySignedBitcoinTransaction {
|
Ok(Arc::new(PartiallySignedTransaction {
|
||||||
internal: Mutex::new(original_psbt),
|
internal: 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.
|
||||||
|
fn fee_amount(&self) -> Option<u64> {
|
||||||
|
self.internal.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.
|
||||||
|
fn fee_rate(&self) -> Option<Arc<FeeRate>> {
|
||||||
|
self.internal.lock().unwrap().fee_rate().map(Arc::new)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Bitcoin wallet.
|
/// A Bitcoin wallet.
|
||||||
@@ -459,7 +476,7 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a transaction with all the wallet’s signers.
|
/// Sign a transaction with all the wallet’s signers.
|
||||||
fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, BdkError> {
|
fn sign(&self, psbt: &PartiallySignedTransaction) -> Result<bool, BdkError> {
|
||||||
let mut psbt = psbt.internal.lock().unwrap();
|
let mut psbt = psbt.internal.lock().unwrap();
|
||||||
self.get_wallet().sign(&mut psbt, SignOptions::default())
|
self.get_wallet().sign(&mut psbt, SignOptions::default())
|
||||||
}
|
}
|
||||||
@@ -531,7 +548,7 @@ enum RbfValue {
|
|||||||
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
||||||
/// transaction details.
|
/// transaction details.
|
||||||
pub struct TxBuilderResult {
|
pub struct TxBuilderResult {
|
||||||
pub psbt: Arc<PartiallySignedBitcoinTransaction>,
|
pub psbt: Arc<PartiallySignedTransaction>,
|
||||||
pub transaction_details: TransactionDetails,
|
pub transaction_details: TransactionDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,7 +775,7 @@ impl TxBuilder {
|
|||||||
tx_builder.enable_rbf();
|
tx_builder.enable_rbf();
|
||||||
}
|
}
|
||||||
RbfValue::Value(nsequence) => {
|
RbfValue::Value(nsequence) => {
|
||||||
tx_builder.enable_rbf_with_sequence(nsequence);
|
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -769,7 +786,7 @@ impl TxBuilder {
|
|||||||
tx_builder
|
tx_builder
|
||||||
.finish()
|
.finish()
|
||||||
.map(|(psbt, tx_details)| TxBuilderResult {
|
.map(|(psbt, tx_details)| TxBuilderResult {
|
||||||
psbt: Arc::new(PartiallySignedBitcoinTransaction {
|
psbt: Arc::new(PartiallySignedTransaction {
|
||||||
internal: Mutex::new(psbt),
|
internal: Mutex::new(psbt),
|
||||||
}),
|
}),
|
||||||
transaction_details: TransactionDetails::from(&tx_details),
|
transaction_details: TransactionDetails::from(&tx_details),
|
||||||
@@ -827,7 +844,7 @@ impl BumpFeeTxBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish building the transaction. Returns the BIP174 PSBT.
|
/// Finish building the transaction. Returns the BIP174 PSBT.
|
||||||
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, BdkError> {
|
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||||
let wallet = wallet.get_wallet();
|
let wallet = wallet.get_wallet();
|
||||||
let txid = Txid::from_str(self.txid.as_str())?;
|
let txid = Txid::from_str(self.txid.as_str())?;
|
||||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||||
@@ -844,23 +861,53 @@ impl BumpFeeTxBuilder {
|
|||||||
tx_builder.enable_rbf();
|
tx_builder.enable_rbf();
|
||||||
}
|
}
|
||||||
RbfValue::Value(nsequence) => {
|
RbfValue::Value(nsequence) => {
|
||||||
tx_builder.enable_rbf_with_sequence(nsequence);
|
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx_builder
|
tx_builder
|
||||||
.finish()
|
.finish()
|
||||||
.map(|(psbt, _)| PartiallySignedBitcoinTransaction {
|
.map(|(psbt, _)| PartiallySignedTransaction {
|
||||||
internal: Mutex::new(psbt),
|
internal: Mutex::new(psbt),
|
||||||
})
|
})
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_mnemonic(word_count: WordCount) -> Result<String, BdkError> {
|
/// Mnemonic phrases are a human-readable version of the private keys.
|
||||||
let mnemonic: GeneratedKey<_, BareCtx> =
|
/// Supported number of words are 12, 15, 18, 21 and 24.
|
||||||
Mnemonic::generate((word_count, Language::English)).unwrap();
|
struct Mnemonic {
|
||||||
Ok(mnemonic.to_string())
|
internal: BdkMnemonic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mnemonic {
|
||||||
|
/// Generates Mnemonic with a random entropy
|
||||||
|
fn new(word_count: WordCount) -> Self {
|
||||||
|
let generated_key: GeneratedKey<_, BareCtx> =
|
||||||
|
BdkMnemonic::generate((word_count, Language::English)).unwrap();
|
||||||
|
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||||
|
Mnemonic { internal: mnemonic }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a Mnemonic with given string
|
||||||
|
fn from_string(mnemonic: String) -> Result<Self, BdkError> {
|
||||||
|
BdkMnemonic::from_str(&mnemonic)
|
||||||
|
.map(|m| Mnemonic { internal: m })
|
||||||
|
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new Mnemonic in the specified language from the given entropy.
|
||||||
|
/// Entropy must be a multiple of 32 bits (4 bytes) and 128-256 bits in length.
|
||||||
|
fn from_entropy(entropy: Vec<u8>) -> Result<Self, BdkError> {
|
||||||
|
BdkMnemonic::from_entropy(entropy.as_slice())
|
||||||
|
.map(|m| Mnemonic { internal: m })
|
||||||
|
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns Mnemonic as string
|
||||||
|
fn as_string(&self) -> String {
|
||||||
|
self.internal.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DerivationPath {
|
struct DerivationPath {
|
||||||
@@ -882,19 +929,18 @@ struct DescriptorSecretKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DescriptorSecretKey {
|
impl DescriptorSecretKey {
|
||||||
fn new(network: Network, mnemonic: String, password: Option<String>) -> Result<Self, BdkError> {
|
fn new(network: Network, mnemonic: Arc<Mnemonic>, password: Option<String>) -> Self {
|
||||||
let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)
|
let mnemonic = mnemonic.internal.clone();
|
||||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
||||||
let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?;
|
|
||||||
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||||
origin: None,
|
origin: None,
|
||||||
xkey: xkey.into_xprv(network).unwrap(),
|
xkey: xkey.into_xprv(network).unwrap(),
|
||||||
derivation_path: BdkDerivationPath::master(),
|
derivation_path: BdkDerivationPath::master(),
|
||||||
wildcard: bdk::descriptor::Wildcard::Unhardened,
|
wildcard: bdk::descriptor::Wildcard::Unhardened,
|
||||||
});
|
});
|
||||||
Ok(Self {
|
Self {
|
||||||
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
|
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||||
@@ -918,7 +964,7 @@ impl DescriptorSecretKey {
|
|||||||
descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key),
|
descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::SinglePriv(_) => {
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -940,7 +986,7 @@ impl DescriptorSecretKey {
|
|||||||
descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key),
|
descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::SinglePriv(_) => {
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -952,7 +998,7 @@ impl DescriptorSecretKey {
|
|||||||
.descriptor_secret_key_mutex
|
.descriptor_secret_key_mutex
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_public(&secp)
|
.to_public(&secp)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Arc::new(DescriptorPublicKey {
|
Arc::new(DescriptorPublicKey {
|
||||||
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
|
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
|
||||||
@@ -966,7 +1012,7 @@ impl DescriptorSecretKey {
|
|||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::SinglePriv(_) => {
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1006,7 +1052,7 @@ impl DescriptorPublicKey {
|
|||||||
descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key),
|
descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::SinglePub(_) => {
|
BdkDescriptorPublicKey::Single(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1028,7 +1074,7 @@ impl DescriptorPublicKey {
|
|||||||
descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key),
|
descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::SinglePub(_) => {
|
BdkDescriptorPublicKey::Single(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1118,9 +1164,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
||||||
let mnemonic =
|
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
||||||
"chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string();
|
DescriptorSecretKey::new(Testnet, Arc::new(mnemonic), None)
|
||||||
DescriptorSecretKey::new(Testnet, mnemonic, None).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_dsk(
|
fn derive_dsk(
|
||||||
@@ -1217,4 +1262,32 @@ mod test {
|
|||||||
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_psbt_fee() {
|
||||||
|
let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||||
|
let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||||
|
let test_wallet = Wallet {
|
||||||
|
wallet_mutex: Mutex::new(funded_wallet),
|
||||||
|
};
|
||||||
|
let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
||||||
|
let tx_builder = TxBuilder::new()
|
||||||
|
.fee_rate(2.0)
|
||||||
|
.drain_wallet()
|
||||||
|
.drain_to(drain_to_address.clone());
|
||||||
|
//dbg!(&tx_builder);
|
||||||
|
assert!(tx_builder.drain_wallet);
|
||||||
|
assert_eq!(tx_builder.drain_to, Some(drain_to_address));
|
||||||
|
|
||||||
|
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||||
|
|
||||||
|
assert!(tx_builder_result.psbt.fee_rate().is_some());
|
||||||
|
assert_eq!(
|
||||||
|
tx_builder_result.psbt.fee_rate().unwrap().as_sat_per_vb(),
|
||||||
|
2.682927
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(tx_builder_result.psbt.fee_amount().is_some());
|
||||||
|
assert_eq!(tx_builder_result.psbt.fee_amount().unwrap(), 220);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
tests/README.md
Normal file
21
tests/README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Integration tests for bdk-ffi
|
||||||
|
|
||||||
|
This contains simple tests to make sure bdk-ffi can be used as a dependency for each of the
|
||||||
|
supported bindings languages.
|
||||||
|
|
||||||
|
To skip integration tests and only run unit tests use `cargo test --lib`.
|
||||||
|
|
||||||
|
To run all tests including integration tests use `CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test`.
|
||||||
|
|
||||||
|
Before running integration tests you must install the following development tools:
|
||||||
|
|
||||||
|
1. [Java](https://openjdk.org/) and [Kotlin](https://kotlinlang.org/),
|
||||||
|
[sdkman](https://sdkman.io/) can help:
|
||||||
|
```shell
|
||||||
|
sdk install java 11.0.16.1-zulu
|
||||||
|
sdk install kotlin 1.7.20`
|
||||||
|
```
|
||||||
|
|
||||||
|
2. [Swift](https://www.swift.org/)
|
||||||
|
|
||||||
|
3. [Python](https://www.python.org/)
|
||||||
8
tests/bindings/test.kts
Normal file
8
tests/bindings/test.kts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* This is a basic test kotlin program that does nothing but confirm that the kotlin bindings compile
|
||||||
|
* and that a program that depends on them will run.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.bitcoindevkit.*
|
||||||
|
|
||||||
|
val network = Network.TESTNET
|
||||||
15
tests/bindings/test.py
Normal file
15
tests/bindings/test.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import unittest
|
||||||
|
from bdk import *
|
||||||
|
|
||||||
|
class TestBdk(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_some_enum(self):
|
||||||
|
network = Network.TESTNET
|
||||||
|
|
||||||
|
def test_some_dict(self):
|
||||||
|
a = AddressInfo(index=42, address="testaddress")
|
||||||
|
self.assertEqual(42, a.index)
|
||||||
|
self.assertEqual("testaddress", a.address)
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
unittest.main()
|
||||||
9
tests/bindings/test.swift
Normal file
9
tests/bindings/test.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* This is a basic test swift program that does nothing but confirm that the swift bindings compile
|
||||||
|
* and that a program that depends on them will run.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import bdk
|
||||||
|
|
||||||
|
let network = Network.testnet
|
||||||
BIN
tests/jna/jna-5.8.0.jar
Normal file
BIN
tests/jna/jna-5.8.0.jar
Normal file
Binary file not shown.
8
tests/test_generated_bindings.rs
Normal file
8
tests/test_generated_bindings.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
uniffi_macros::build_foreign_language_testcases!(
|
||||||
|
["src/bdk.udl",],
|
||||||
|
[
|
||||||
|
"tests/bindings/test.kts",
|
||||||
|
"tests/bindings/test.swift",
|
||||||
|
"tests/bindings/test.py"
|
||||||
|
]
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user