Compare commits

..

1 Commits

Author SHA1 Message Date
Steve Myers
e6cf423721 Update uniffi dependencies to 0.21.0 2022-10-21 13:29:18 -05:00
10 changed files with 52 additions and 215 deletions

View File

@@ -41,7 +41,7 @@ jobs:
if: ${{ matrix.rust.clippy }}
run: cargo clippy --all-targets -- -D warnings
- name: Test
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test
run: cargo test
fmt:
name: Rust fmt

View File

@@ -1,31 +1,23 @@
[package]
name = "bdk-ffi"
version = "0.11.0"
version = "0.10.0"
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
[workspace]
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]
crate-type = ["staticlib", "cdylib"]
name = "bdkffi"
[dependencies]
bdk = { version = "0.24", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
bdk = { version = "0.23", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
[build-dependencies]
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*

View File

@@ -1,5 +1,6 @@
namespace bdk {
[Throws=BdkError]
string generate_mnemonic(WordCount word_count);
};
[Error]
@@ -32,9 +33,9 @@ enum BdkError {
"ProgressUpdateError",
"InvalidOutpoint",
"Descriptor",
"AddressValidator",
"Encode",
"Miniscript",
"MiniscriptPsbt",
"Bip32",
"Secp256k1",
"Json",
@@ -137,7 +138,7 @@ interface Blockchain {
constructor(BlockchainConfig config);
[Throws=BdkError]
void broadcast([ByRef] PartiallySignedTransaction psbt);
void broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
[Throws=BdkError]
u32 get_height();
@@ -188,7 +189,7 @@ interface Wallet {
Balance get_balance();
[Throws=BdkError]
boolean sign([ByRef] PartiallySignedTransaction psbt);
boolean sign([ByRef] PartiallySignedBitcoinTransaction psbt);
[Throws=BdkError]
sequence<TransactionDetails> list_transactions();
@@ -202,14 +203,7 @@ interface Wallet {
sequence<LocalUtxo> list_unspent();
};
interface FeeRate {
[Name=from_sat_per_vb]
constructor(float sat_per_vb);
float as_sat_per_vb();
};
interface PartiallySignedTransaction {
interface PartiallySignedBitcoinTransaction {
[Throws=BdkError]
constructor(string psbt_base64);
@@ -220,15 +214,11 @@ interface PartiallySignedTransaction {
sequence<u8> extract_tx();
[Throws=BdkError]
PartiallySignedTransaction combine(PartiallySignedTransaction other);
u64? fee_amount();
FeeRate? fee_rate();
PartiallySignedBitcoinTransaction combine(PartiallySignedBitcoinTransaction other);
};
dictionary TxBuilderResult {
PartiallySignedTransaction psbt;
PartiallySignedBitcoinTransaction psbt;
TransactionDetails transaction_details;
};
@@ -281,19 +271,7 @@ interface BumpFeeTxBuilder {
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
[Throws=BdkError]
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();
PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet);
};
interface DerivationPath {
@@ -302,7 +280,8 @@ interface DerivationPath {
};
interface DescriptorSecretKey {
constructor(Network network, Mnemonic mnemonic, string? password);
[Throws=BdkError]
constructor(Network network, string mnemonic, string? password);
[Throws=BdkError]
DescriptorSecretKey derive(DerivationPath path);

View File

@@ -3,8 +3,7 @@ use bdk::bitcoin::hashes::hex::ToHex;
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
use bdk::bitcoin::util::psbt::serialize::Serialize;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::bitcoin::Sequence;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::GetBlockHash;
@@ -16,13 +15,12 @@ use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress};
use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration};
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
use bdk::descriptor::DescriptorXKey;
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
use bdk::keys::bip39::{Language, Mnemonic, WordCount};
use bdk::keys::{
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
};
use bdk::miniscript::BareCtx;
use bdk::psbt::PsbtUtils;
use bdk::wallet::tx_builder::ChangeSpendPolicy;
use bdk::wallet::AddressIndex as BdkAddressIndex;
use bdk::wallet::AddressInfo as BdkAddressInfo;
@@ -211,7 +209,7 @@ impl Blockchain {
self.blockchain_mutex.lock().expect("blockchain")
}
fn broadcast(&self, psbt: &PartiallySignedTransaction) -> Result<(), BdkError> {
fn broadcast(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<(), BdkError> {
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
self.get_blockchain().broadcast(&tx)
}
@@ -342,15 +340,14 @@ impl fmt::Debug for ProgressHolder {
}
#[derive(Debug)]
pub struct PartiallySignedTransaction {
internal: Mutex<BdkPartiallySignedTransaction>,
pub struct PartiallySignedBitcoinTransaction {
internal: Mutex<PartiallySignedTransaction>,
}
impl PartiallySignedTransaction {
impl PartiallySignedBitcoinTransaction {
fn new(psbt_base64: String) -> Result<Self, BdkError> {
let psbt: BdkPartiallySignedTransaction =
BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
Ok(PartiallySignedTransaction {
let psbt: PartiallySignedTransaction = PartiallySignedTransaction::from_str(&psbt_base64)?;
Ok(PartiallySignedBitcoinTransaction {
internal: Mutex::new(psbt),
})
}
@@ -381,30 +378,16 @@ impl PartiallySignedTransaction {
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
fn combine(
&self,
other: Arc<PartiallySignedTransaction>,
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
other: Arc<PartiallySignedBitcoinTransaction>,
) -> Result<Arc<PartiallySignedBitcoinTransaction>, BdkError> {
let other_psbt = other.internal.lock().unwrap().clone();
let mut original_psbt = self.internal.lock().unwrap().clone();
original_psbt.combine(other_psbt)?;
Ok(Arc::new(PartiallySignedTransaction {
Ok(Arc::new(PartiallySignedBitcoinTransaction {
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.
@@ -476,7 +459,7 @@ impl Wallet {
}
/// Sign a transaction with all the wallets signers.
fn sign(&self, psbt: &PartiallySignedTransaction) -> Result<bool, BdkError> {
fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, BdkError> {
let mut psbt = psbt.internal.lock().unwrap();
self.get_wallet().sign(&mut psbt, SignOptions::default())
}
@@ -548,7 +531,7 @@ enum RbfValue {
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
/// transaction details.
pub struct TxBuilderResult {
pub psbt: Arc<PartiallySignedTransaction>,
pub psbt: Arc<PartiallySignedBitcoinTransaction>,
pub transaction_details: TransactionDetails,
}
@@ -775,7 +758,7 @@ impl TxBuilder {
tx_builder.enable_rbf();
}
RbfValue::Value(nsequence) => {
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
tx_builder.enable_rbf_with_sequence(nsequence);
}
}
}
@@ -786,7 +769,7 @@ impl TxBuilder {
tx_builder
.finish()
.map(|(psbt, tx_details)| TxBuilderResult {
psbt: Arc::new(PartiallySignedTransaction {
psbt: Arc::new(PartiallySignedBitcoinTransaction {
internal: Mutex::new(psbt),
}),
transaction_details: TransactionDetails::from(&tx_details),
@@ -844,7 +827,7 @@ impl BumpFeeTxBuilder {
}
/// Finish building the transaction. Returns the BIP174 PSBT.
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, BdkError> {
let wallet = wallet.get_wallet();
let txid = Txid::from_str(self.txid.as_str())?;
let mut tx_builder = wallet.build_fee_bump(txid)?;
@@ -861,53 +844,23 @@ impl BumpFeeTxBuilder {
tx_builder.enable_rbf();
}
RbfValue::Value(nsequence) => {
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
tx_builder.enable_rbf_with_sequence(nsequence);
}
}
}
tx_builder
.finish()
.map(|(psbt, _)| PartiallySignedTransaction {
.map(|(psbt, _)| PartiallySignedBitcoinTransaction {
internal: Mutex::new(psbt),
})
.map(Arc::new)
}
}
/// Mnemonic phrases are a human-readable version of the private keys.
/// Supported number of words are 12, 15, 18, 21 and 24.
struct Mnemonic {
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()
}
fn generate_mnemonic(word_count: WordCount) -> Result<String, BdkError> {
let mnemonic: GeneratedKey<_, BareCtx> =
Mnemonic::generate((word_count, Language::English)).unwrap();
Ok(mnemonic.to_string())
}
struct DerivationPath {
@@ -929,18 +882,19 @@ struct DescriptorSecretKey {
}
impl DescriptorSecretKey {
fn new(network: Network, mnemonic: Arc<Mnemonic>, password: Option<String>) -> Self {
let mnemonic = mnemonic.internal.clone();
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
fn new(network: Network, mnemonic: String, password: Option<String>) -> Result<Self, BdkError> {
let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)
.map_err(|e| BdkError::Generic(e.to_string()))?;
let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?;
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
origin: None,
xkey: xkey.into_xprv(network).unwrap(),
derivation_path: BdkDerivationPath::master(),
wildcard: bdk::descriptor::Wildcard::Unhardened,
});
Self {
Ok(Self {
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
}
})
}
fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
@@ -964,7 +918,7 @@ impl DescriptorSecretKey {
descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key),
}))
}
BdkDescriptorSecretKey::Single(_) => {
BdkDescriptorSecretKey::SinglePriv(_) => {
unreachable!()
}
}
@@ -986,7 +940,7 @@ impl DescriptorSecretKey {
descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key),
})
}
BdkDescriptorSecretKey::Single(_) => {
BdkDescriptorSecretKey::SinglePriv(_) => {
unreachable!()
}
}
@@ -998,7 +952,7 @@ impl DescriptorSecretKey {
.descriptor_secret_key_mutex
.lock()
.unwrap()
.to_public(&secp)
.as_public(&secp)
.unwrap();
Arc::new(DescriptorPublicKey {
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
@@ -1012,7 +966,7 @@ impl DescriptorSecretKey {
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
}
BdkDescriptorSecretKey::Single(_) => {
BdkDescriptorSecretKey::SinglePriv(_) => {
unreachable!()
}
};
@@ -1052,7 +1006,7 @@ impl DescriptorPublicKey {
descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key),
}))
}
BdkDescriptorPublicKey::Single(_) => {
BdkDescriptorPublicKey::SinglePub(_) => {
unreachable!()
}
}
@@ -1074,7 +1028,7 @@ impl DescriptorPublicKey {
descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key),
})
}
BdkDescriptorPublicKey::Single(_) => {
BdkDescriptorPublicKey::SinglePub(_) => {
unreachable!()
}
}
@@ -1164,8 +1118,9 @@ mod test {
}
fn get_descriptor_secret_key() -> DescriptorSecretKey {
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();
DescriptorSecretKey::new(Testnet, Arc::new(mnemonic), None)
let mnemonic =
"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, mnemonic, None).unwrap()
}
fn derive_dsk(
@@ -1262,32 +1217,4 @@ mod test {
"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);
}
}

View File

@@ -1,21 +0,0 @@
# 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/)

View File

@@ -1,8 +0,0 @@
/*
* 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

View File

@@ -1,15 +0,0 @@
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()

View File

@@ -1,9 +0,0 @@
/*
* 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

Binary file not shown.

View File

@@ -1,8 +0,0 @@
uniffi_macros::build_foreign_language_testcases!(
["src/bdk.udl",],
[
"tests/bindings/test.kts",
"tests/bindings/test.swift",
"tests/bindings/test.py"
]
);