feat: upgrade bdk to 1.0.0-alpha.2
This is a big change that updates some of our build infrastructure as well as upgrading the bdk dependency. It adds the simple new_no_persist constructor on the wallet as well as the blocking esplora client.
This commit is contained in:
parent
743862fb60
commit
790aee9b3b
14
.github/workflows/audit.yml
vendored
14
.github/workflows/audit.yml
vendored
@ -12,8 +12,14 @@ jobs:
|
||||
security_audit:
|
||||
name: Security audit
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Run audit"
|
||||
run: |
|
||||
cargo install cargo-audit
|
||||
cargo-audit audit
|
||||
|
51
.github/workflows/cont_integration.yml
vendored
51
.github/workflows/cont_integration.yml
vendored
@ -9,22 +9,26 @@ on:
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
name: Build and test
|
||||
name: "Build and test"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.71.0 # STABLE
|
||||
- version: 1.67.0
|
||||
clippy: true
|
||||
- version: 1.61.0 # MSRV
|
||||
# TODO 1: Should we keep this? We'll need to pin dependencies
|
||||
# - version: 1.61.0 # MSRV
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate cache key
|
||||
- name: "Generate cache key"
|
||||
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||
|
||||
- name: Cache
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@ -33,53 +37,56 @@ jobs:
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set default toolchain
|
||||
- name: "Set default toolchain"
|
||||
run: rustup default ${{ matrix.rust.version }}
|
||||
|
||||
- name: Set profile
|
||||
- name: "Set profile"
|
||||
run: rustup set profile minimal
|
||||
|
||||
- name: Add clippy
|
||||
- name: "Add clippy"
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: rustup component add clippy
|
||||
|
||||
- name: Update toolchain
|
||||
- name: "Update toolchain"
|
||||
run: rustup update
|
||||
|
||||
- name: Pin dependencies for MSRV
|
||||
- name: "Pin dependencies for MSRV"
|
||||
if: matrix.rust.version == '1.61.0'
|
||||
run: |
|
||||
cargo update -p hashlink --precise "0.8.1"
|
||||
cargo update -p tokio --precise "1.29.1"
|
||||
cargo update -p flate2 --precise "1.0.26"
|
||||
- name: Build
|
||||
- name: "Build"
|
||||
run: cargo build
|
||||
|
||||
- name: Clippy
|
||||
- name: "Clippy"
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
|
||||
|
||||
- name: Test
|
||||
- name: "Test"
|
||||
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test --features uniffi/bindgen-tests
|
||||
|
||||
fmt:
|
||||
name: Rust fmt
|
||||
runs-on: ubuntu-latest
|
||||
name: "Rust fmt"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set default toolchain
|
||||
- name: "Set default toolchain"
|
||||
run: rustup default nightly
|
||||
|
||||
- name: Set profile
|
||||
- name: "Set profile"
|
||||
run: rustup set profile minimal
|
||||
|
||||
- name: Add rustfmt
|
||||
- name: "Add rustfmt"
|
||||
run: rustup component add rustfmt
|
||||
|
||||
- name: Update toolchain
|
||||
- name: "Update toolchain"
|
||||
run: rustup update
|
||||
|
||||
- name: Check fmt
|
||||
- name: "Check fmt"
|
||||
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
||||
|
1
.github/workflows/publish-python.yaml
vendored
1
.github/workflows/publish-python.yaml
vendored
@ -29,6 +29,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
# TODO 2: Other CI workflows use explicit Rust compiler versions, I think we should do the same here
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
39
.github/workflows/test-swift.yaml
vendored
39
.github/workflows/test-swift.yaml
vendored
@ -12,44 +12,15 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build and test"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
- name: "Build Swift package"
|
||||
run: bash ./bdk-swift/build-local-swift.sh
|
||||
|
||||
- name: Install Rust targets
|
||||
run: |
|
||||
rustup install nightly-x86_64-apple-darwin
|
||||
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
|
||||
- name: Run bdk-ffi-bindgen
|
||||
working-directory: bdk-ffi
|
||||
run: cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
||||
|
||||
- name: Build bdk-ffi for x86_64-apple-darwin
|
||||
run: cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
- name: Build bdk-ffi for aarch64-apple-darwin
|
||||
run: cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
- name: Create lipo-macos
|
||||
run: |
|
||||
mkdir -p target/lipo-macos/release-smaller
|
||||
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
||||
|
||||
- name: Create bdkFFI.xcframework
|
||||
working-directory: bdk-swift
|
||||
run: |
|
||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||
cp ../target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||
rm Sources/BitcoinDevkit/bdkFFI.modulemap
|
||||
|
||||
- name: Run Swift tests
|
||||
- name: "Run Swift tests"
|
||||
working-directory: bdk-swift
|
||||
run: swift test
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -1,12 +0,0 @@
|
||||
[workspace]
|
||||
members = ["bdk-ffi"]
|
||||
default-members = ["bdk-ffi"]
|
||||
exclude = ["api-docs", "bdk-android", "bdk-jvm", "bdk-python", "bdk-swift"]
|
||||
|
||||
[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
|
@ -18,64 +18,42 @@ import java.io.File
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AndroidLibTest {
|
||||
|
||||
private fun getTestDataDir(): String {
|
||||
val context = ApplicationProvider.getApplicationContext<Application>()
|
||||
return context.getDir("bdk-test", MODE_PRIVATE).toString()
|
||||
}
|
||||
|
||||
private fun cleanupTestDataDir(testDataDir: String) {
|
||||
File(testDataDir).deleteRecursively()
|
||||
}
|
||||
|
||||
class LogProgress : Progress {
|
||||
private val log: Logger = LoggerFactory.getLogger(AndroidLibTest::class.java)
|
||||
|
||||
override fun update(progress: Float, message: String?) {
|
||||
log.debug("Syncing...")
|
||||
}
|
||||
}
|
||||
|
||||
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
|
||||
private val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
100u,
|
||||
true,
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun memoryWalletNewAddress() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val address = wallet.getAddress(AddressIndex.New).address.asString()
|
||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
||||
@Test
|
||||
fun testNetwork() {
|
||||
val signetNetwork = Network.SIGNET
|
||||
}
|
||||
|
||||
@Test
|
||||
fun memoryWalletSyncGetBalance() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
val descriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sqliteWalletSyncGetBalance() {
|
||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
cleanupTestDataDir(testDataDir)
|
||||
fun testUsedWallet() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val (index, address, keychain) = wallet.getAddress(AddressIndex.LastUnused)
|
||||
println("Address ${address.asString()} at index $index")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
|
||||
assert(wallet.getBalance().total() == 0uL)
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun testSyncedBalance() {
|
||||
// val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
// val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
// val esploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
// // val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
|
||||
// val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
// wallet.applyUpdate(update)
|
||||
// println("Balance: ${wallet.getBalance().total()}")
|
||||
// }
|
||||
}
|
||||
|
@ -117,15 +117,15 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||
|
||||
into("arm64-v8a") {
|
||||
from("${project.projectDir}/../../target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("x86_64") {
|
||||
from("${project.projectDir}/../../target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("armeabi-v7a") {
|
||||
from("${project.projectDir}/../../target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
doLast {
|
||||
@ -137,6 +137,12 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
||||
dependsOn(moveNativeAndroidLibs)
|
||||
|
||||
// val libraryPath = "${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so"
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
// The code above worked for uniffi 0.24.3 using the --library flag
|
||||
// The code below works for uniffi 0.23.0
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
|
658
Cargo.lock → bdk-ffi/Cargo.lock
generated
658
Cargo.lock → bdk-ffi/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "0.30.0"
|
||||
version = "1.0.0-alpha.2"
|
||||
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@ -17,12 +17,30 @@ path = "uniffi-bindgen.rs"
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk = { version = "0.28.2", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
|
||||
uniffi = { version = "0.23.0" }
|
||||
bdk = { version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
|
||||
|
||||
# TODO 22: The bdk_esplora crate uses esplora_client which uses reqwest for async. By default it uses the system
|
||||
# openssl library, which is creating problems for cross-compilation. I'd rather use rustls, but it's hidden
|
||||
# behind a feature flag. We need to look into whether openssl-sys is really required by bdk or if using rustls
|
||||
# would work just as well. This here is a temporary workaround which removes the async feature on the bdk_esplora crate.
|
||||
# See PR #1179 https://github.com/bitcoindevkit/bdk/pull/1179 for the fix in bdk.
|
||||
# bdk = { git = "https://github.com/thunderbiscuit/bdk.git", branch = "test-rust-tls", version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
|
||||
# bdk_esplora = { git = "https://github.com/thunderbiscuit/bdk.git", branch = "test-rust-tls", version = "0.4.0", package = "bdk_esplora", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
|
||||
|
||||
bdk_esplora = { version = "0.4.0", default-features = false, features = ["std", "blocking"] }
|
||||
uniffi = { version = "=0.23.0" }
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.23.0", features = ["build"] }
|
||||
uniffi = { version = "=0.23.0", features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
uniffi = { version = "0.23.0", features = ["bindgen-tests"] }
|
||||
uniffi = { version = "=0.23.0", features = ["bindgen-tests"] }
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[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 = "debuginfo" # Partially strip symbols from binary
|
||||
|
@ -1,13 +1,21 @@
|
||||
namespace bdk {
|
||||
namespace bdk {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum KeychainKind {
|
||||
"External",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - wallet module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Error]
|
||||
enum BdkError {
|
||||
"InvalidU32Bytes",
|
||||
"Generic",
|
||||
"MissingCachedScripts",
|
||||
"ScriptDoesntHaveAddressForm",
|
||||
"NoRecipients",
|
||||
"NoUtxosSelected",
|
||||
"OutputBelowDustLimit",
|
||||
@ -27,25 +35,26 @@ enum BdkError {
|
||||
"SpendingPolicyRequired",
|
||||
"InvalidPolicyPathError",
|
||||
"Signer",
|
||||
"InvalidNetwork",
|
||||
"InvalidProgressValue",
|
||||
"ProgressUpdateError",
|
||||
"InvalidOutpoint",
|
||||
"Descriptor",
|
||||
"Encode",
|
||||
"Miniscript",
|
||||
"MiniscriptPsbt",
|
||||
"Bip32",
|
||||
"Secp256k1",
|
||||
"Json",
|
||||
"Hex",
|
||||
"Psbt",
|
||||
"PsbtParse",
|
||||
"Electrum",
|
||||
"Esplora",
|
||||
"Sled",
|
||||
"Rusqlite",
|
||||
"Rpc",
|
||||
};
|
||||
|
||||
interface Balance {
|
||||
u64 immature();
|
||||
|
||||
u64 trusted_pending();
|
||||
|
||||
u64 untrusted_pending();
|
||||
|
||||
u64 confirmed();
|
||||
|
||||
u64 trusted_spendable();
|
||||
|
||||
u64 total();
|
||||
};
|
||||
|
||||
dictionary AddressInfo {
|
||||
@ -59,9 +68,28 @@ interface AddressIndex {
|
||||
New();
|
||||
LastUnused();
|
||||
Peek(u32 index);
|
||||
Reset(u32 index);
|
||||
};
|
||||
|
||||
interface Wallet {
|
||||
[Name=new_no_persist, Throws=BdkError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network);
|
||||
|
||||
AddressInfo get_address(AddressIndex address_index);
|
||||
|
||||
Network network();
|
||||
|
||||
Balance get_balance();
|
||||
|
||||
[Throws=BdkError]
|
||||
void apply_update(Update update);
|
||||
};
|
||||
|
||||
interface Update {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - bitcoin reexports
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum Network {
|
||||
"Bitcoin",
|
||||
"Testnet",
|
||||
@ -69,45 +97,6 @@ enum Network {
|
||||
"Regtest",
|
||||
};
|
||||
|
||||
dictionary SledDbConfiguration {
|
||||
string path;
|
||||
string tree_name;
|
||||
};
|
||||
|
||||
dictionary SqliteDbConfiguration {
|
||||
string path;
|
||||
};
|
||||
|
||||
dictionary Balance {
|
||||
u64 immature;
|
||||
u64 trusted_pending;
|
||||
u64 untrusted_pending;
|
||||
u64 confirmed;
|
||||
u64 spendable;
|
||||
u64 total;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface DatabaseConfig {
|
||||
Memory();
|
||||
Sled(SledDbConfiguration config);
|
||||
Sqlite(SqliteDbConfiguration config);
|
||||
};
|
||||
|
||||
dictionary TransactionDetails {
|
||||
Transaction? transaction;
|
||||
u64? fee;
|
||||
u64 received;
|
||||
u64 sent;
|
||||
string txid;
|
||||
BlockTime? confirmation_time;
|
||||
};
|
||||
|
||||
dictionary BlockTime {
|
||||
u32 height;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
enum WordCount {
|
||||
"Words12",
|
||||
"Words15",
|
||||
@ -116,260 +105,20 @@ enum WordCount {
|
||||
"Words24",
|
||||
};
|
||||
|
||||
dictionary ElectrumConfig {
|
||||
string url;
|
||||
string? socks5;
|
||||
u8 retry;
|
||||
u8? timeout;
|
||||
u64 stop_gap;
|
||||
boolean validate_domain;
|
||||
};
|
||||
|
||||
dictionary EsploraConfig {
|
||||
string base_url;
|
||||
string? proxy;
|
||||
u8? concurrency;
|
||||
u64 stop_gap;
|
||||
u64? timeout;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface Auth {
|
||||
None();
|
||||
UserPass(string username, string password);
|
||||
Cookie(string file);
|
||||
};
|
||||
|
||||
dictionary RpcSyncParams {
|
||||
u64 start_script_count;
|
||||
u64 start_time;
|
||||
boolean force_start_time;
|
||||
u64 poll_rate_sec;
|
||||
};
|
||||
|
||||
dictionary RpcConfig {
|
||||
string url;
|
||||
Auth auth;
|
||||
Network network;
|
||||
string wallet_name;
|
||||
RpcSyncParams? sync_params;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface BlockchainConfig {
|
||||
Electrum(ElectrumConfig config);
|
||||
Esplora(EsploraConfig config);
|
||||
Rpc(RpcConfig config);
|
||||
};
|
||||
|
||||
interface Blockchain {
|
||||
interface Address {
|
||||
[Throws=BdkError]
|
||||
constructor(BlockchainConfig config);
|
||||
|
||||
[Throws=BdkError]
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
|
||||
[Throws=BdkError]
|
||||
FeeRate estimate_fee(u64 target);
|
||||
|
||||
[Throws=BdkError]
|
||||
u32 get_height();
|
||||
|
||||
[Throws=BdkError]
|
||||
string get_block_hash(u32 height);
|
||||
};
|
||||
|
||||
callback interface Progress {
|
||||
void update(f32 progress, string? message);
|
||||
};
|
||||
|
||||
dictionary OutPoint {
|
||||
string txid;
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
dictionary TxIn {
|
||||
OutPoint previous_output;
|
||||
Script script_sig;
|
||||
u32 sequence;
|
||||
sequence<sequence<u8>> witness;
|
||||
};
|
||||
|
||||
dictionary TxOut {
|
||||
u64 value;
|
||||
Script script_pubkey;
|
||||
};
|
||||
|
||||
enum KeychainKind {
|
||||
"External",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
dictionary LocalUtxo {
|
||||
OutPoint outpoint;
|
||||
TxOut txout;
|
||||
KeychainKind keychain;
|
||||
boolean is_spent;
|
||||
};
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
u64 amount;
|
||||
};
|
||||
|
||||
interface Wallet {
|
||||
[Throws=BdkError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network, DatabaseConfig database_config);
|
||||
constructor(string address, Network network);
|
||||
|
||||
Network network();
|
||||
|
||||
[Throws=BdkError]
|
||||
AddressInfo get_address(AddressIndex address_index);
|
||||
string to_qr_uri();
|
||||
|
||||
[Throws=BdkError]
|
||||
AddressInfo get_internal_address(AddressIndex address_index);
|
||||
|
||||
[Throws=BdkError]
|
||||
boolean is_mine(Script script);
|
||||
|
||||
[Throws=BdkError]
|
||||
sequence<LocalUtxo> list_unspent();
|
||||
|
||||
[Throws=BdkError]
|
||||
sequence<TransactionDetails> list_transactions(boolean include_raw);
|
||||
|
||||
[Throws=BdkError]
|
||||
Balance get_balance();
|
||||
|
||||
[Throws=BdkError]
|
||||
boolean sign([ByRef] PartiallySignedTransaction psbt, SignOptions? sign_options);
|
||||
|
||||
[Throws=BdkError]
|
||||
void sync([ByRef] Blockchain blockchain, Progress? progress);
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb]
|
||||
constructor(float sat_per_vb);
|
||||
|
||||
float as_sat_per_vb();
|
||||
};
|
||||
|
||||
dictionary SignOptions {
|
||||
boolean trust_witness_utxo;
|
||||
u32? assume_height;
|
||||
boolean allow_all_sighashes;
|
||||
boolean remove_partial_sigs;
|
||||
boolean try_finalize;
|
||||
boolean sign_with_tap_internal_key;
|
||||
boolean allow_grinding;
|
||||
};
|
||||
|
||||
interface Transaction {
|
||||
[Throws=BdkError]
|
||||
constructor(sequence<u8> transaction_bytes);
|
||||
|
||||
string txid();
|
||||
|
||||
u64 weight();
|
||||
|
||||
u64 size();
|
||||
|
||||
u64 vsize();
|
||||
|
||||
sequence<u8> serialize();
|
||||
|
||||
boolean is_coin_base();
|
||||
|
||||
boolean is_explicitly_rbf();
|
||||
|
||||
boolean is_lock_time_enabled();
|
||||
|
||||
i32 version();
|
||||
|
||||
u32 lock_time();
|
||||
|
||||
sequence<TxIn> input();
|
||||
|
||||
sequence<TxOut> output();
|
||||
};
|
||||
|
||||
interface PartiallySignedTransaction {
|
||||
[Throws=BdkError]
|
||||
constructor(string psbt_base64);
|
||||
|
||||
string serialize();
|
||||
|
||||
string txid();
|
||||
|
||||
Transaction extract_tx();
|
||||
|
||||
[Throws=BdkError]
|
||||
PartiallySignedTransaction combine(PartiallySignedTransaction other);
|
||||
|
||||
u64? fee_amount();
|
||||
|
||||
FeeRate? fee_rate();
|
||||
|
||||
string json_serialize();
|
||||
};
|
||||
|
||||
dictionary TxBuilderResult {
|
||||
PartiallySignedTransaction psbt;
|
||||
TransactionDetails transaction_details;
|
||||
};
|
||||
|
||||
interface TxBuilder {
|
||||
constructor();
|
||||
|
||||
TxBuilder add_recipient(Script script, u64 amount);
|
||||
|
||||
TxBuilder add_unspendable(OutPoint unspendable);
|
||||
|
||||
TxBuilder add_utxo(OutPoint outpoint);
|
||||
|
||||
TxBuilder add_utxos(sequence<OutPoint> outpoints);
|
||||
|
||||
TxBuilder do_not_spend_change();
|
||||
|
||||
TxBuilder manually_selected_only();
|
||||
|
||||
TxBuilder only_spend_change();
|
||||
|
||||
TxBuilder unspendable(sequence<OutPoint> unspendable);
|
||||
|
||||
TxBuilder fee_rate(float sat_per_vbyte);
|
||||
|
||||
TxBuilder fee_absolute(u64 fee_amount);
|
||||
|
||||
TxBuilder drain_wallet();
|
||||
|
||||
TxBuilder drain_to(Script script);
|
||||
|
||||
TxBuilder enable_rbf();
|
||||
|
||||
TxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
TxBuilder add_data(sequence<u8> data);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||
|
||||
[Throws=BdkError]
|
||||
TxBuilderResult finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface BumpFeeTxBuilder {
|
||||
constructor(string txid, float new_fee_rate);
|
||||
|
||||
BumpFeeTxBuilder allow_shrinking(string address);
|
||||
|
||||
BumpFeeTxBuilder enable_rbf();
|
||||
|
||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=BdkError]
|
||||
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||
};
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - descriptor module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface Mnemonic {
|
||||
constructor(WordCount word_count);
|
||||
@ -453,55 +202,10 @@ interface Descriptor {
|
||||
string as_string_private();
|
||||
};
|
||||
|
||||
interface Address {
|
||||
[Throws=BdkError]
|
||||
constructor(string address);
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_esplora crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Name=from_script, Throws=BdkError]
|
||||
constructor(Script script, Network network);
|
||||
|
||||
Payload payload();
|
||||
|
||||
Network network();
|
||||
|
||||
Script script_pubkey();
|
||||
|
||||
string to_qr_uri();
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface Payload {
|
||||
PubkeyHash(sequence<u8> pubkey_hash);
|
||||
|
||||
ScriptHash(sequence<u8> script_hash);
|
||||
|
||||
WitnessProgram(WitnessVersion version, sequence<u8> program);
|
||||
};
|
||||
|
||||
enum WitnessVersion {
|
||||
"V0",
|
||||
"V1",
|
||||
"V2",
|
||||
"V3",
|
||||
"V4",
|
||||
"V5",
|
||||
"V6",
|
||||
"V7",
|
||||
"V8",
|
||||
"V9",
|
||||
"V10",
|
||||
"V11",
|
||||
"V12",
|
||||
"V13",
|
||||
"V14",
|
||||
"V15",
|
||||
"V16"
|
||||
};
|
||||
|
||||
interface Script {
|
||||
constructor(sequence<u8> raw_output_script);
|
||||
|
||||
sequence<u8> to_bytes();
|
||||
interface EsploraClient {
|
||||
constructor(string url);
|
||||
};
|
||||
|
@ -1,201 +0,0 @@
|
||||
// use crate::BlockchainConfig;
|
||||
use crate::{BdkError, Transaction};
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
|
||||
use bdk::blockchain::rpc::Auth as BdkAuth;
|
||||
use bdk::blockchain::rpc::RpcSyncParams as BdkRpcSyncParams;
|
||||
use bdk::blockchain::Blockchain as BdkBlockchain;
|
||||
use bdk::blockchain::GetBlockHash;
|
||||
use bdk::blockchain::GetHeight;
|
||||
use bdk::blockchain::{
|
||||
electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig,
|
||||
rpc::RpcConfig as BdkRpcConfig, ConfigurableBlockchain,
|
||||
};
|
||||
use bdk::FeeRate;
|
||||
use std::convert::{From, TryFrom};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
pub(crate) struct Blockchain {
|
||||
inner_mutex: Mutex<AnyBlockchain>,
|
||||
}
|
||||
|
||||
impl Blockchain {
|
||||
pub(crate) fn new(blockchain_config: BlockchainConfig) -> Result<Self, BdkError> {
|
||||
let any_blockchain_config = match blockchain_config {
|
||||
BlockchainConfig::Electrum { config } => {
|
||||
AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
|
||||
retry: config.retry,
|
||||
socks5: config.socks5,
|
||||
timeout: config.timeout,
|
||||
url: config.url,
|
||||
stop_gap: usize::try_from(config.stop_gap).unwrap(),
|
||||
validate_domain: config.validate_domain,
|
||||
})
|
||||
}
|
||||
BlockchainConfig::Esplora { config } => {
|
||||
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
|
||||
base_url: config.base_url,
|
||||
proxy: config.proxy,
|
||||
concurrency: config.concurrency,
|
||||
stop_gap: usize::try_from(config.stop_gap).unwrap(),
|
||||
timeout: config.timeout,
|
||||
})
|
||||
}
|
||||
BlockchainConfig::Rpc { config } => AnyBlockchainConfig::Rpc(BdkRpcConfig {
|
||||
url: config.url,
|
||||
auth: config.auth.into(),
|
||||
network: config.network,
|
||||
wallet_name: config.wallet_name,
|
||||
sync_params: config.sync_params.map(|p| p.into()),
|
||||
}),
|
||||
};
|
||||
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
|
||||
Ok(Self {
|
||||
inner_mutex: Mutex::new(blockchain),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_blockchain(&self) -> MutexGuard<AnyBlockchain> {
|
||||
self.inner_mutex.lock().expect("blockchain")
|
||||
}
|
||||
|
||||
pub(crate) fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
|
||||
let tx = &transaction.inner;
|
||||
self.get_blockchain().broadcast(tx)
|
||||
}
|
||||
|
||||
pub(crate) fn estimate_fee(&self, target: u64) -> Result<Arc<FeeRate>, BdkError> {
|
||||
let result: Result<FeeRate, bdk::Error> =
|
||||
self.get_blockchain().estimate_fee(target as usize);
|
||||
result.map(Arc::new)
|
||||
}
|
||||
|
||||
pub(crate) fn get_height(&self) -> Result<u32, BdkError> {
|
||||
self.get_blockchain().get_height()
|
||||
}
|
||||
|
||||
pub(crate) fn get_block_hash(&self, height: u32) -> Result<String, BdkError> {
|
||||
self.get_blockchain()
|
||||
.get_block_hash(u64::from(height))
|
||||
.map(|hash| hash.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for an ElectrumBlockchain
|
||||
pub struct ElectrumConfig {
|
||||
/// URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with ssl:// or tcp:// and include a port
|
||||
/// e.g. ssl://electrum.blockstream.info:60002
|
||||
pub url: String,
|
||||
/// URL of the socks5 proxy server or a Tor service
|
||||
pub socks5: Option<String>,
|
||||
/// Request retry count
|
||||
pub retry: u8,
|
||||
/// Request timeout (seconds)
|
||||
pub timeout: Option<u8>,
|
||||
/// Stop searching addresses for transactions after finding an unused gap of this length
|
||||
pub stop_gap: u64,
|
||||
/// Validate the domain when using SSL
|
||||
pub validate_domain: bool,
|
||||
}
|
||||
|
||||
/// Configuration for an EsploraBlockchain
|
||||
pub struct EsploraConfig {
|
||||
/// Base URL of the esplora service
|
||||
/// e.g. https://blockstream.info/api/
|
||||
pub base_url: String,
|
||||
/// Optional URL of the proxy to use to make requests to the Esplora server
|
||||
/// The string should be formatted as: <protocol>://<user>:<password>@host:<port>.
|
||||
/// Note that the format of this value and the supported protocols change slightly between the
|
||||
/// sync version of esplora (using ureq) and the async version (using reqwest). For more
|
||||
/// details check with the documentation of the two crates. Both of them are compiled with
|
||||
/// the socks feature enabled.
|
||||
/// The proxy is ignored when targeting wasm32.
|
||||
pub proxy: Option<String>,
|
||||
/// Number of parallel requests sent to the esplora service (default: 4)
|
||||
pub concurrency: Option<u8>,
|
||||
/// Stop searching addresses for transactions after finding an unused gap of this length.
|
||||
pub stop_gap: u64,
|
||||
/// Socket timeout.
|
||||
pub timeout: Option<u64>,
|
||||
}
|
||||
|
||||
pub enum Auth {
|
||||
/// No authentication
|
||||
None,
|
||||
/// Authentication with username and password, usually [Auth::Cookie] should be preferred
|
||||
UserPass {
|
||||
/// Username
|
||||
username: String,
|
||||
/// Password
|
||||
password: String,
|
||||
},
|
||||
/// Authentication with a cookie file
|
||||
Cookie {
|
||||
/// Cookie file
|
||||
file: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Auth> for BdkAuth {
|
||||
fn from(auth: Auth) -> Self {
|
||||
match auth {
|
||||
Auth::None => BdkAuth::None,
|
||||
Auth::UserPass { username, password } => BdkAuth::UserPass { username, password },
|
||||
Auth::Cookie { file } => BdkAuth::Cookie {
|
||||
file: PathBuf::from(file),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync parameters for Bitcoin Core RPC.
|
||||
///
|
||||
/// In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
|
||||
/// `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
|
||||
/// how the `importdescriptors` RPC calls are to be made.
|
||||
pub struct RpcSyncParams {
|
||||
/// The minimum number of scripts to scan for on initial sync.
|
||||
pub start_script_count: u64,
|
||||
/// Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
|
||||
pub start_time: u64,
|
||||
/// Forces every sync to use `start_time` as import timestamp.
|
||||
pub force_start_time: bool,
|
||||
/// RPC poll rate (in seconds) to get state updates.
|
||||
pub poll_rate_sec: u64,
|
||||
}
|
||||
|
||||
impl From<RpcSyncParams> for BdkRpcSyncParams {
|
||||
fn from(params: RpcSyncParams) -> Self {
|
||||
BdkRpcSyncParams {
|
||||
start_script_count: params.start_script_count as usize,
|
||||
start_time: params.start_time,
|
||||
force_start_time: params.force_start_time,
|
||||
poll_rate_sec: params.poll_rate_sec,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RpcBlockchain configuration options
|
||||
pub struct RpcConfig {
|
||||
/// The bitcoin node url
|
||||
pub url: String,
|
||||
/// The bitcoin node authentication mechanism
|
||||
pub auth: Auth,
|
||||
/// The network we are using (it will be checked the bitcoin node network matches this)
|
||||
pub network: Network,
|
||||
/// The wallet name in the bitcoin node, consider using [crate::wallet::wallet_name_from_descriptor] for this
|
||||
pub wallet_name: String,
|
||||
/// Sync parameters
|
||||
pub sync_params: Option<RpcSyncParams>,
|
||||
}
|
||||
|
||||
/// Type that can contain any of the blockchain configurations defined by the library.
|
||||
pub enum BlockchainConfig {
|
||||
/// Electrum client
|
||||
Electrum { config: ElectrumConfig },
|
||||
/// Esplora client
|
||||
Esplora { config: EsploraConfig },
|
||||
/// Bitcoin Core RPC client
|
||||
Rpc { config: RpcConfig },
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
|
||||
|
||||
/// Type that can contain any of the database configurations defined by the library
|
||||
/// This allows storing a single configuration that can be loaded into an AnyDatabaseConfig
|
||||
/// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime
|
||||
/// will find this particularly useful.
|
||||
pub enum DatabaseConfig {
|
||||
/// Memory database has no config
|
||||
Memory,
|
||||
/// Simple key-value embedded database based on sled
|
||||
Sled { config: SledDbConfiguration },
|
||||
/// Sqlite embedded database using rusqlite
|
||||
Sqlite { config: SqliteDbConfiguration },
|
||||
}
|
@ -1,29 +1,32 @@
|
||||
use crate::{BdkError, DescriptorPublicKey, DescriptorSecretKey};
|
||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||
use bdk::bitcoin::util::bip32::Fingerprint;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
|
||||
use bdk::keys::{
|
||||
DescriptorPublicKey as BdkDescriptorPublicKey, DescriptorSecretKey as BdkDescriptorSecretKey,
|
||||
};
|
||||
use bdk::bitcoin::bip32::Fingerprint;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||
use bdk::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||
use bdk::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||
use bdk::template::{
|
||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
||||
DescriptorTemplate,
|
||||
};
|
||||
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(crate) struct Descriptor {
|
||||
pub(crate) extended_descriptor: ExtendedDescriptor,
|
||||
pub(crate) key_map: KeyMap,
|
||||
pub struct Descriptor {
|
||||
pub extended_descriptor: ExtendedDescriptor,
|
||||
pub key_map: KeyMap,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, BdkError> {
|
||||
let secp = Secp256k1::new();
|
||||
let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?;
|
||||
let (extended_descriptor, key_map) =
|
||||
descriptor.into_wallet_descriptor(&secp, network.into())?;
|
||||
Ok(Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
@ -38,16 +41,20 @@ impl Descriptor {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip44(derivable_key, keychain_kind).build(network).unwrap();
|
||||
let (extended_descriptor, key_map, _) = Bip44(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -63,11 +70,14 @@ impl Descriptor {
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip44Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
@ -75,7 +85,7 @@ impl Descriptor {
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -89,16 +99,20 @@ impl Descriptor {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip49(derivable_key, keychain_kind).build(network).unwrap();
|
||||
let (extended_descriptor, key_map, _) = Bip49(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -114,11 +128,14 @@ impl Descriptor {
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip49Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
@ -126,7 +143,7 @@ impl Descriptor {
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -140,16 +157,20 @@ impl Descriptor {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip84(derivable_key, keychain_kind).build(network).unwrap();
|
||||
let (extended_descriptor, key_map, _) = Bip84(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -165,11 +186,14 @@ impl Descriptor {
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip84Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
@ -177,7 +201,7 @@ impl Descriptor {
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -191,16 +215,20 @@ impl Descriptor {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip86(derivable_key, keychain_kind).build(network).unwrap();
|
||||
let (extended_descriptor, key_map, _) = Bip86(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -216,11 +244,14 @@ impl Descriptor {
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip86Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
@ -228,7 +259,7 @@ impl Descriptor {
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@ -245,12 +276,11 @@ impl Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::database::DatabaseConfig;
|
||||
use crate::*;
|
||||
use assert_matches::assert_matches;
|
||||
use bdk::descriptor::DescriptorError::Key;
|
||||
@ -385,26 +415,4 @@ mod test {
|
||||
bdk::Error::Descriptor(Key(InvalidNetwork))
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn test_wallet_from_descriptor() {
|
||||
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap();
|
||||
let wallet1 = Wallet::new(
|
||||
Arc::new(Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap()),
|
||||
None,
|
||||
Network::Testnet,
|
||||
DatabaseConfig::Memory
|
||||
);
|
||||
let wallet2 = Wallet::new(
|
||||
Arc::new(descriptor1),
|
||||
None,
|
||||
Network::Bitcoin,
|
||||
DatabaseConfig::Memory,
|
||||
);
|
||||
// Creating a wallet using a Descriptor with an extended key that doesn't match the network provided in the wallet constructor will throw and InvalidNetwork Error
|
||||
assert!(wallet1.is_ok());
|
||||
assert_matches!(
|
||||
wallet2.unwrap_err(),
|
||||
bdk::Error::Descriptor(Key(InvalidNetwork))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
18
bdk-ffi/src/esplora.rs
Normal file
18
bdk-ffi/src/esplora.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||
|
||||
pub struct EsploraClient(BlockingClient);
|
||||
|
||||
impl EsploraClient {
|
||||
pub fn new(url: String) -> Self {
|
||||
let client = Builder::new(url.as_str()).build_blocking().unwrap();
|
||||
Self(client)
|
||||
}
|
||||
|
||||
// pub fn scan();
|
||||
|
||||
// pub fn sync();
|
||||
|
||||
// pub fn broadcast();
|
||||
|
||||
// pub fn estimate_fee();
|
||||
}
|
@ -1,21 +1,24 @@
|
||||
use crate::BdkError;
|
||||
|
||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor::DescriptorXKey;
|
||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
|
||||
use bdk::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
use bdk::bitcoin::secp256k1::rand;
|
||||
use bdk::bitcoin::secp256k1::rand::Rng;
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||
use bdk::keys::{
|
||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||
};
|
||||
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};
|
||||
|
||||
/// Mnemonic phrases are a human-readable version of the private keys.
|
||||
/// Supported number of words are 12, 15, 18, 21 and 24.
|
||||
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.
|
||||
pub(crate) struct Mnemonic {
|
||||
inner: BdkMnemonic,
|
||||
}
|
||||
@ -23,8 +26,13 @@ pub(crate) struct Mnemonic {
|
||||
impl Mnemonic {
|
||||
/// Generates Mnemonic with a random entropy
|
||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||
// TODO 4: I DON'T KNOW IF THIS IS A DECENT WAY TO GENERATE ENTROPY PLEASE CONFIRM
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut entropy = [0u8; 32];
|
||||
rng.fill(&mut entropy);
|
||||
|
||||
let generated_key: GeneratedKey<_, BareCtx> =
|
||||
BdkMnemonic::generate((word_count, Language::English)).unwrap();
|
||||
BdkMnemonic::generate_with_entropy((word_count, Language::English), entropy).unwrap();
|
||||
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||
Mnemonic { inner: mnemonic }
|
||||
}
|
||||
@ -65,7 +73,7 @@ impl DerivationPath {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DescriptorSecretKey {
|
||||
pub struct DescriptorSecretKey {
|
||||
pub(crate) inner: BdkDescriptorSecretKey,
|
||||
}
|
||||
|
||||
@ -75,9 +83,9 @@ impl DescriptorSecretKey {
|
||||
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: None,
|
||||
xkey: xkey.into_xprv(network).unwrap(),
|
||||
xkey: xkey.into_xprv(network.into()).unwrap(),
|
||||
derivation_path: BdkDerivationPath::master(),
|
||||
wildcard: bdk::descriptor::Wildcard::Unhardened,
|
||||
wildcard: Wildcard::Unhardened,
|
||||
});
|
||||
Self {
|
||||
inner: descriptor_secret_key,
|
||||
@ -97,6 +105,9 @@ impl DescriptorSecretKey {
|
||||
let descriptor_secret_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
@ -113,8 +124,8 @@ impl DescriptorSecretKey {
|
||||
inner: derived_descriptor_secret_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -123,6 +134,9 @@ impl DescriptorSecretKey {
|
||||
let descriptor_secret_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
@ -135,8 +149,8 @@ impl DescriptorSecretKey {
|
||||
inner: extended_descriptor_secret_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -153,10 +167,13 @@ impl DescriptorSecretKey {
|
||||
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
||||
let inner = &self.inner;
|
||||
let secret_bytes: Vec<u8> = match inner.deref() {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
@ -170,7 +187,7 @@ impl DescriptorSecretKey {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DescriptorPublicKey {
|
||||
pub struct DescriptorPublicKey {
|
||||
pub(crate) inner: BdkDescriptorPublicKey,
|
||||
}
|
||||
|
||||
@ -189,6 +206,9 @@ impl DescriptorPublicKey {
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
|
||||
match descriptor_public_key.deref() {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
@ -205,8 +225,8 @@ impl DescriptorPublicKey {
|
||||
inner: derived_descriptor_public_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi xpub".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -215,6 +235,9 @@ impl DescriptorPublicKey {
|
||||
let descriptor_public_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_public_key.deref() {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||
@ -227,8 +250,8 @@ impl DescriptorPublicKey {
|
||||
inner: extended_descriptor_public_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi xpub".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -237,21 +260,21 @@ impl DescriptorPublicKey {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
//
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use crate::BdkError;
|
||||
use bdk::bitcoin::hashes::hex::ToHex;
|
||||
// use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use bdk::bitcoin::Network;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn get_inner() -> 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(Network::Testnet, Arc::new(mnemonic), None)
|
||||
DescriptorSecretKey::new(Network::Testnet.into(), Arc::new(mnemonic), None)
|
||||
}
|
||||
|
||||
fn derive_dsk(
|
||||
@ -359,13 +382,16 @@ mod test {
|
||||
assert!(derived_dpk.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retrieve_master_secret_key() {
|
||||
let master_dpk = get_inner();
|
||||
let master_private_key = master_dpk.secret_bytes().to_hex();
|
||||
assert_eq!(
|
||||
master_private_key,
|
||||
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||
)
|
||||
}
|
||||
// TODO 7: It appears that the to_hex() method is not available anymore.
|
||||
// Look into the correct way to pull the hex out of the DescriptorSecretKey.
|
||||
// Note: ToHex was removed in bitcoin_hashes 0.12.0
|
||||
// #[test]
|
||||
// fn test_retrieve_master_secret_key() {
|
||||
// let master_dpk = get_inner();
|
||||
// let master_private_key = master_dpk.secret_bytes().to_hex();
|
||||
// assert_eq!(
|
||||
// master_private_key,
|
||||
// "e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
@ -1,52 +1,73 @@
|
||||
mod blockchain;
|
||||
mod database;
|
||||
mod descriptor;
|
||||
mod esplora;
|
||||
mod keys;
|
||||
mod psbt;
|
||||
mod wallet;
|
||||
|
||||
use crate::blockchain::{
|
||||
Auth, Blockchain, BlockchainConfig, ElectrumConfig, EsploraConfig, RpcConfig, RpcSyncParams,
|
||||
};
|
||||
use crate::database::DatabaseConfig;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::keys::DerivationPath;
|
||||
use crate::keys::{DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use crate::psbt::PartiallySignedTransaction;
|
||||
use crate::wallet::SignOptions;
|
||||
use crate::wallet::{BumpFeeTxBuilder, TxBuilder, Wallet};
|
||||
use bdk::bitcoin::blockdata::script::Script as BdkScript;
|
||||
use bdk::bitcoin::blockdata::transaction::TxIn as BdkTxIn;
|
||||
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
|
||||
use bdk::bitcoin::consensus::Decodable;
|
||||
use bdk::bitcoin::psbt::serialize::Serialize;
|
||||
use bdk::bitcoin::util::address::{Payload as BdkPayload, WitnessVersion};
|
||||
use bdk::bitcoin::{
|
||||
Address as BdkAddress, Network, OutPoint as BdkOutPoint, Transaction as BdkTransaction, Txid,
|
||||
};
|
||||
use bdk::blockchain::Progress as BdkProgress;
|
||||
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk::bitcoin::Address as BdkAddress;
|
||||
use bdk::bitcoin::Network as BdkNetwork;
|
||||
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
||||
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
||||
use bdk::LocalUtxo as BdkLocalUtxo;
|
||||
use bdk::TransactionDetails as BdkTransactionDetails;
|
||||
use bdk::{Balance as BdkBalance, BlockTime, Error as BdkError, FeeRate, KeychainKind};
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use bdk::wallet::Balance as BdkBalance;
|
||||
use bdk::Error as BdkError;
|
||||
use bdk::KeychainKind;
|
||||
use std::sync::Arc;
|
||||
|
||||
// TODO 6: Why are these imports required?
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::esplora::EsploraClient;
|
||||
use crate::keys::DerivationPath;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use crate::keys::Mnemonic;
|
||||
use crate::wallet::Update;
|
||||
use crate::wallet::Wallet;
|
||||
use bdk::keys::bip39::WordCount;
|
||||
// use bdk_esplora::EsploraExt;
|
||||
|
||||
uniffi::include_scaffolding!("bdk");
|
||||
|
||||
/// A output script and an amount of satoshis.
|
||||
pub struct ScriptAmount {
|
||||
pub script: Arc<Script>,
|
||||
pub amount: u64,
|
||||
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 output script and an amount of satoshis.
|
||||
// pub struct ScriptAmount {
|
||||
// pub script: Arc<Script>,
|
||||
// pub amount: u64,
|
||||
// }
|
||||
//
|
||||
/// A derived address and the index it was found at.
|
||||
pub struct AddressInfo {
|
||||
/// Child index of this address.
|
||||
@ -61,7 +82,7 @@ impl From<BdkAddressInfo> for AddressInfo {
|
||||
fn from(address_info: BdkAddressInfo) -> Self {
|
||||
AddressInfo {
|
||||
index: address_info.index,
|
||||
address: Arc::new(Address::from(address_info.address)),
|
||||
address: Arc::new(address_info.address.into()),
|
||||
keychain: address_info.keychain,
|
||||
}
|
||||
}
|
||||
@ -84,14 +105,6 @@ pub enum AddressIndex {
|
||||
/// Use with caution, if an index is given that is less than the current descriptor index
|
||||
/// then the returned address may have already been used.
|
||||
Peek { index: u32 },
|
||||
/// Return the address for a specific descriptor index and reset the current descriptor index
|
||||
/// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value.
|
||||
/// Use with caution, if an index is given that is less than the current descriptor index
|
||||
/// then the returned address and subsequent addresses returned by calls to `AddressIndex::New`
|
||||
/// and `AddressIndex::LastUsed` may have already been used. Also if the index is reset to a
|
||||
/// value earlier than the [`Blockchain`] stop_gap (default is 20) then a
|
||||
/// larger stop_gap should be used to monitor for all possibly used addresses.
|
||||
Reset { index: u32 },
|
||||
}
|
||||
|
||||
impl From<AddressIndex> for BdkAddressIndex {
|
||||
@ -100,301 +113,350 @@ impl From<AddressIndex> for BdkAddressIndex {
|
||||
AddressIndex::New => BdkAddressIndex::New,
|
||||
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||
AddressIndex::Peek { index } => BdkAddressIndex::Peek(index),
|
||||
AddressIndex::Reset { index } => BdkAddressIndex::Reset(index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wallet transaction
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct TransactionDetails {
|
||||
pub transaction: Option<Arc<Transaction>>,
|
||||
/// Transaction id.
|
||||
pub txid: String,
|
||||
/// Received value (sats)
|
||||
/// Sum of owned outputs of this transaction.
|
||||
pub received: u64,
|
||||
/// Sent value (sats)
|
||||
/// Sum of owned inputs of this transaction.
|
||||
pub sent: u64,
|
||||
/// Fee value (sats) if confirmed.
|
||||
/// The availability of the fee depends on the backend. It's never None with an Electrum
|
||||
/// Server backend, but it could be None with a Bitcoin RPC node without txindex that receive
|
||||
/// funds while offline.
|
||||
pub fee: Option<u64>,
|
||||
/// If the transaction is confirmed, contains height and timestamp of the block containing the
|
||||
/// transaction, unconfirmed transaction contains `None`.
|
||||
pub confirmation_time: Option<BlockTime>,
|
||||
}
|
||||
|
||||
impl From<BdkTransactionDetails> for TransactionDetails {
|
||||
fn from(tx_details: BdkTransactionDetails) -> Self {
|
||||
let optional_tx: Option<Arc<Transaction>> =
|
||||
tx_details.transaction.map(|tx| Arc::new(tx.into()));
|
||||
|
||||
TransactionDetails {
|
||||
transaction: optional_tx,
|
||||
fee: tx_details.fee,
|
||||
txid: tx_details.txid.to_string(),
|
||||
received: tx_details.received,
|
||||
sent: tx_details.sent,
|
||||
confirmation_time: tx_details.confirmation_time,
|
||||
// TODO 9: Peek is not correctly implemented
|
||||
impl From<&AddressIndex> for BdkAddressIndex {
|
||||
fn from(address_index: &AddressIndex) -> Self {
|
||||
match address_index {
|
||||
AddressIndex::New => BdkAddressIndex::New,
|
||||
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||
AddressIndex::Peek { index } => BdkAddressIndex::Peek(*index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a transaction output.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OutPoint {
|
||||
/// The referenced transaction's txid.
|
||||
txid: String,
|
||||
/// The index of the referenced output in its transaction's vout.
|
||||
vout: u32,
|
||||
}
|
||||
|
||||
impl From<&OutPoint> for BdkOutPoint {
|
||||
fn from(outpoint: &OutPoint) -> Self {
|
||||
BdkOutPoint {
|
||||
txid: Txid::from_str(&outpoint.txid).unwrap(),
|
||||
vout: outpoint.vout,
|
||||
impl From<BdkAddressIndex> for AddressIndex {
|
||||
fn from(address_index: BdkAddressIndex) -> Self {
|
||||
match address_index {
|
||||
BdkAddressIndex::New => AddressIndex::New,
|
||||
BdkAddressIndex::LastUnused => AddressIndex::LastUnused,
|
||||
_ => panic!("Mmmm not working"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BdkAddressIndex> for AddressIndex {
|
||||
fn from(address_index: &BdkAddressIndex) -> Self {
|
||||
match address_index {
|
||||
BdkAddressIndex::New => AddressIndex::New,
|
||||
BdkAddressIndex::LastUnused => AddressIndex::LastUnused,
|
||||
_ => panic!("Mmmm not working"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /// A wallet transaction
|
||||
// #[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
// pub struct TransactionDetails {
|
||||
// pub transaction: Option<Arc<Transaction>>,
|
||||
// /// Transaction id.
|
||||
// pub txid: String,
|
||||
// /// Received value (sats)
|
||||
// /// Sum of owned outputs of this transaction.
|
||||
// pub received: u64,
|
||||
// /// Sent value (sats)
|
||||
// /// Sum of owned inputs of this transaction.
|
||||
// pub sent: u64,
|
||||
// /// Fee value (sats) if confirmed.
|
||||
// /// The availability of the fee depends on the backend. It's never None with an Electrum
|
||||
// /// Server backend, but it could be None with a Bitcoin RPC node without txindex that receive
|
||||
// /// funds while offline.
|
||||
// pub fee: Option<u64>,
|
||||
// /// If the transaction is confirmed, contains height and timestamp of the block containing the
|
||||
// /// transaction, unconfirmed transaction contains `None`.
|
||||
// pub confirmation_time: Option<BlockTime>,
|
||||
// }
|
||||
|
||||
//
|
||||
// impl From<BdkTransactionDetails> for TransactionDetails {
|
||||
// fn from(tx_details: BdkTransactionDetails) -> Self {
|
||||
// let optional_tx: Option<Arc<Transaction>> =
|
||||
// tx_details.transaction.map(|tx| Arc::new(tx.into()));
|
||||
//
|
||||
// TransactionDetails {
|
||||
// transaction: optional_tx,
|
||||
// fee: tx_details.fee,
|
||||
// txid: tx_details.txid.to_string(),
|
||||
// received: tx_details.received,
|
||||
// sent: tx_details.sent,
|
||||
// confirmation_time: tx_details.confirmation_time,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// A reference to a transaction output.
|
||||
// #[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
// pub struct OutPoint {
|
||||
// /// The referenced transaction's txid.
|
||||
// txid: String,
|
||||
// /// The index of the referenced output in its transaction's vout.
|
||||
// vout: u32,
|
||||
// }
|
||||
//
|
||||
// impl From<&OutPoint> for BdkOutPoint {
|
||||
// fn from(outpoint: &OutPoint) -> Self {
|
||||
// BdkOutPoint {
|
||||
// txid: Txid::from_str(&outpoint.txid).unwrap(),
|
||||
// vout: outpoint.vout,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct Balance {
|
||||
// All coinbase outputs not yet matured
|
||||
pub immature: u64,
|
||||
/// Unconfirmed UTXOs generated by a wallet tx
|
||||
pub trusted_pending: u64,
|
||||
/// Unconfirmed UTXOs received from an external wallet
|
||||
pub untrusted_pending: u64,
|
||||
/// Confirmed and immediately spendable balance
|
||||
pub confirmed: u64,
|
||||
/// Get sum of trusted_pending and confirmed coins
|
||||
pub spendable: u64,
|
||||
/// Get the whole balance visible to the wallet
|
||||
pub total: u64,
|
||||
pub inner: BdkBalance,
|
||||
}
|
||||
|
||||
impl From<BdkBalance> for Balance {
|
||||
fn from(bdk_balance: BdkBalance) -> Self {
|
||||
Balance {
|
||||
immature: bdk_balance.immature,
|
||||
trusted_pending: bdk_balance.trusted_pending,
|
||||
untrusted_pending: bdk_balance.untrusted_pending,
|
||||
confirmed: bdk_balance.confirmed,
|
||||
spendable: bdk_balance.get_spendable(),
|
||||
total: bdk_balance.get_total(),
|
||||
}
|
||||
impl Balance {
|
||||
/// All coinbase outputs not yet matured.
|
||||
fn immature(&self) -> u64 {
|
||||
self.inner.immature
|
||||
}
|
||||
|
||||
/// Unconfirmed UTXOs generated by a wallet tx.
|
||||
fn trusted_pending(&self) -> u64 {
|
||||
self.inner.trusted_pending
|
||||
}
|
||||
|
||||
/// Unconfirmed UTXOs received from an external wallet.
|
||||
fn untrusted_pending(&self) -> u64 {
|
||||
self.inner.untrusted_pending
|
||||
}
|
||||
|
||||
/// Confirmed and immediately spendable balance.
|
||||
fn confirmed(&self) -> u64 {
|
||||
self.inner.confirmed
|
||||
}
|
||||
|
||||
/// Get sum of trusted_pending and confirmed coins.
|
||||
fn trusted_spendable(&self) -> u64 {
|
||||
self.inner.trusted_spendable()
|
||||
}
|
||||
|
||||
/// Get the whole balance visible to the wallet.
|
||||
fn total(&self) -> u64 {
|
||||
self.inner.total()
|
||||
}
|
||||
}
|
||||
|
||||
/// A transaction output, which defines new coins to be created from old ones.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxOut {
|
||||
/// The value of the output, in satoshis.
|
||||
value: u64,
|
||||
/// The address of the output.
|
||||
script_pubkey: Arc<Script>,
|
||||
}
|
||||
// impl From<BdkBalance> for Balance {
|
||||
// fn from(bdk_balance: BdkBalance) -> Self {
|
||||
// Balance { inner: bdk_balance }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<&BdkTxOut> for TxOut {
|
||||
fn from(tx_out: &BdkTxOut) -> Self {
|
||||
TxOut {
|
||||
value: tx_out.value,
|
||||
script_pubkey: Arc::new(Script {
|
||||
inner: tx_out.script_pubkey.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
// /// A transaction output, which defines new coins to be created from old ones.
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct TxOut {
|
||||
// /// The value of the output, in satoshis.
|
||||
// value: u64,
|
||||
// /// The address of the output.
|
||||
// script_pubkey: Arc<Script>,
|
||||
// }
|
||||
//
|
||||
// impl From<&BdkTxOut> for TxOut {
|
||||
// fn from(tx_out: &BdkTxOut) -> Self {
|
||||
// TxOut {
|
||||
// value: tx_out.value,
|
||||
// script_pubkey: Arc::new(Script {
|
||||
// inner: tx_out.script_pubkey.clone(),
|
||||
// }),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub struct LocalUtxo {
|
||||
// outpoint: OutPoint,
|
||||
// txout: TxOut,
|
||||
// keychain: KeychainKind,
|
||||
// is_spent: bool,
|
||||
// }
|
||||
//
|
||||
// impl From<BdkLocalUtxo> for LocalUtxo {
|
||||
// fn from(local_utxo: BdkLocalUtxo) -> Self {
|
||||
// LocalUtxo {
|
||||
// outpoint: OutPoint {
|
||||
// txid: local_utxo.outpoint.txid.to_string(),
|
||||
// vout: local_utxo.outpoint.vout,
|
||||
// },
|
||||
// txout: TxOut {
|
||||
// value: local_utxo.txout.value,
|
||||
// script_pubkey: Arc::new(Script {
|
||||
// inner: local_utxo.txout.script_pubkey,
|
||||
// }),
|
||||
// },
|
||||
// keychain: local_utxo.keychain,
|
||||
// is_spent: local_utxo.is_spent,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Trait that logs at level INFO every update received (if any).
|
||||
// pub trait Progress: Send + Sync + 'static {
|
||||
// /// Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an
|
||||
// /// optional text message that can be displayed to the user.
|
||||
// fn update(&self, progress: f32, message: Option<String>);
|
||||
// }
|
||||
//
|
||||
// struct ProgressHolder {
|
||||
// progress: Box<dyn Progress>,
|
||||
// }
|
||||
//
|
||||
// impl BdkProgress for ProgressHolder {
|
||||
// fn update(&self, progress: f32, message: Option<String>) -> Result<(), BdkError> {
|
||||
// self.progress.update(progress, message);
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Debug for ProgressHolder {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// f.debug_struct("ProgressHolder").finish_non_exhaustive()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct TxIn {
|
||||
// pub previous_output: OutPoint,
|
||||
// pub script_sig: Arc<Script>,
|
||||
// pub sequence: u32,
|
||||
// pub witness: Vec<Vec<u8>>,
|
||||
// }
|
||||
//
|
||||
// impl From<&BdkTxIn> for TxIn {
|
||||
// fn from(tx_in: &BdkTxIn) -> Self {
|
||||
// TxIn {
|
||||
// previous_output: OutPoint {
|
||||
// txid: tx_in.previous_output.txid.to_string(),
|
||||
// vout: tx_in.previous_output.vout,
|
||||
// },
|
||||
// script_sig: Arc::new(Script {
|
||||
// inner: tx_in.script_sig.clone(),
|
||||
// }),
|
||||
// sequence: tx_in.sequence.0,
|
||||
// witness: tx_in.witness.to_vec(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// A Bitcoin transaction.
|
||||
// #[derive(Debug, Clone, PartialEq, Eq)]
|
||||
// pub struct Transaction {
|
||||
// inner: BdkTransaction,
|
||||
// }
|
||||
//
|
||||
// impl Transaction {
|
||||
// fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
|
||||
// let mut decoder = Cursor::new(transaction_bytes);
|
||||
// let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
|
||||
// Ok(Transaction { inner: tx })
|
||||
// }
|
||||
//
|
||||
// fn txid(&self) -> String {
|
||||
// self.inner.txid().to_string()
|
||||
// }
|
||||
//
|
||||
// fn weight(&self) -> u64 {
|
||||
// self.inner.weight() as u64
|
||||
// }
|
||||
//
|
||||
// fn size(&self) -> u64 {
|
||||
// self.inner.size() as u64
|
||||
// }
|
||||
//
|
||||
// fn vsize(&self) -> u64 {
|
||||
// self.inner.vsize() as u64
|
||||
// }
|
||||
//
|
||||
// fn serialize(&self) -> Vec<u8> {
|
||||
// self.inner.serialize()
|
||||
// }
|
||||
//
|
||||
// fn is_coin_base(&self) -> bool {
|
||||
// self.inner.is_coin_base()
|
||||
// }
|
||||
//
|
||||
// fn is_explicitly_rbf(&self) -> bool {
|
||||
// self.inner.is_explicitly_rbf()
|
||||
// }
|
||||
//
|
||||
// fn is_lock_time_enabled(&self) -> bool {
|
||||
// self.inner.is_lock_time_enabled()
|
||||
// }
|
||||
//
|
||||
// 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()
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct LocalUtxo {
|
||||
outpoint: OutPoint,
|
||||
txout: TxOut,
|
||||
keychain: KeychainKind,
|
||||
is_spent: bool,
|
||||
}
|
||||
|
||||
impl From<BdkLocalUtxo> for LocalUtxo {
|
||||
fn from(local_utxo: BdkLocalUtxo) -> Self {
|
||||
LocalUtxo {
|
||||
outpoint: OutPoint {
|
||||
txid: local_utxo.outpoint.txid.to_string(),
|
||||
vout: local_utxo.outpoint.vout,
|
||||
},
|
||||
txout: TxOut {
|
||||
value: local_utxo.txout.value,
|
||||
script_pubkey: Arc::new(Script {
|
||||
inner: local_utxo.txout.script_pubkey,
|
||||
}),
|
||||
},
|
||||
keychain: local_utxo.keychain,
|
||||
is_spent: local_utxo.is_spent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that logs at level INFO every update received (if any).
|
||||
pub trait Progress: Send + Sync + 'static {
|
||||
/// Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an
|
||||
/// optional text message that can be displayed to the user.
|
||||
fn update(&self, progress: f32, message: Option<String>);
|
||||
}
|
||||
|
||||
struct ProgressHolder {
|
||||
progress: Box<dyn Progress>,
|
||||
}
|
||||
|
||||
impl BdkProgress for ProgressHolder {
|
||||
fn update(&self, progress: f32, message: Option<String>) -> Result<(), BdkError> {
|
||||
self.progress.update(progress, message);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ProgressHolder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ProgressHolder").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxIn {
|
||||
pub previous_output: OutPoint,
|
||||
pub script_sig: Arc<Script>,
|
||||
pub sequence: u32,
|
||||
pub witness: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl From<&BdkTxIn> for TxIn {
|
||||
fn from(tx_in: &BdkTxIn) -> Self {
|
||||
TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: tx_in.previous_output.txid.to_string(),
|
||||
vout: tx_in.previous_output.vout,
|
||||
},
|
||||
script_sig: Arc::new(Script {
|
||||
inner: tx_in.script_sig.clone(),
|
||||
}),
|
||||
sequence: tx_in.sequence.0,
|
||||
witness: tx_in.witness.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin transaction.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Transaction {
|
||||
inner: BdkTransaction,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
|
||||
let mut decoder = Cursor::new(transaction_bytes);
|
||||
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
|
||||
Ok(Transaction { inner: tx })
|
||||
}
|
||||
|
||||
fn txid(&self) -> String {
|
||||
self.inner.txid().to_string()
|
||||
}
|
||||
|
||||
fn weight(&self) -> u64 {
|
||||
self.inner.weight() as u64
|
||||
}
|
||||
|
||||
fn size(&self) -> u64 {
|
||||
self.inner.size() as u64
|
||||
}
|
||||
|
||||
fn vsize(&self) -> u64 {
|
||||
self.inner.vsize() as u64
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.inner.serialize()
|
||||
}
|
||||
|
||||
fn is_coin_base(&self) -> bool {
|
||||
self.inner.is_coin_base()
|
||||
}
|
||||
|
||||
fn is_explicitly_rbf(&self) -> bool {
|
||||
self.inner.is_explicitly_rbf()
|
||||
}
|
||||
|
||||
fn is_lock_time_enabled(&self) -> bool {
|
||||
self.inner.is_lock_time_enabled()
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
// impl From<BdkTransaction> for Transaction {
|
||||
// fn from(tx: BdkTransaction) -> Self {
|
||||
// Transaction { inner: tx }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// A Bitcoin address.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Address {
|
||||
inner: BdkAddress,
|
||||
inner: BdkAddress<NetworkChecked>,
|
||||
}
|
||||
|
||||
impl Address {
|
||||
fn new(address: String) -> Result<Self, BdkError> {
|
||||
BdkAddress::from_str(address.as_str())
|
||||
.map(|a| Address { inner: a })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
// 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(),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
fn network(&self) -> Network {
|
||||
self.inner.network
|
||||
self.inner.network.into()
|
||||
}
|
||||
|
||||
fn script_pubkey(&self) -> Arc<Script> {
|
||||
Arc::new(Script {
|
||||
inner: self.inner.script_pubkey(),
|
||||
})
|
||||
}
|
||||
// fn script_pubkey(&self) -> Arc<Script> {
|
||||
// Arc::new(Script {
|
||||
// inner: self.inner.script_pubkey(),
|
||||
// })
|
||||
// }
|
||||
|
||||
fn to_qr_uri(&self) -> String {
|
||||
self.inner.to_qr_uri()
|
||||
@ -411,91 +473,97 @@ impl From<BdkAddress> for Address {
|
||||
}
|
||||
}
|
||||
|
||||
/// The method used to produce an address.
|
||||
#[derive(Debug)]
|
||||
pub enum Payload {
|
||||
/// P2PKH address.
|
||||
PubkeyHash { pubkey_hash: Vec<u8> },
|
||||
/// P2SH address.
|
||||
ScriptHash { script_hash: Vec<u8> },
|
||||
/// Segwit address.
|
||||
WitnessProgram {
|
||||
/// The witness program version.
|
||||
version: WitnessVersion,
|
||||
/// The witness program.
|
||||
program: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A Bitcoin script.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Script {
|
||||
inner: BdkScript,
|
||||
}
|
||||
|
||||
impl Script {
|
||||
fn new(raw_output_script: Vec<u8>) -> Self {
|
||||
let script: BdkScript = BdkScript::from(raw_output_script);
|
||||
Script { inner: script }
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
self.inner.to_bytes()
|
||||
impl From<Address> for BdkAddress {
|
||||
fn from(address: Address) -> Self {
|
||||
address.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkScript> for Script {
|
||||
fn from(bdk_script: BdkScript) -> Self {
|
||||
Script { inner: bdk_script }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum RbfValue {
|
||||
Default,
|
||||
Value(u32),
|
||||
}
|
||||
|
||||
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
||||
/// transaction details.
|
||||
pub struct TxBuilderResult {
|
||||
pub(crate) psbt: Arc<PartiallySignedTransaction>,
|
||||
pub transaction_details: TransactionDetails,
|
||||
}
|
||||
|
||||
uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Transaction;
|
||||
use crate::Network::Regtest;
|
||||
use crate::{Address, Payload};
|
||||
use assert_matches::assert_matches;
|
||||
use bdk::bitcoin::hashes::hex::FromHex;
|
||||
use bdk::bitcoin::util::address::WitnessVersion;
|
||||
|
||||
// Verify that bdk-ffi Transaction can be created from valid bytes and serialized back into the same bytes.
|
||||
#[test]
|
||||
fn test_transaction_serde() {
|
||||
let test_tx_bytes = Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700").unwrap();
|
||||
let new_tx_from_bytes = Transaction::new(test_tx_bytes.clone()).unwrap();
|
||||
let serialized_tx_to_bytes = new_tx_from_bytes.serialize();
|
||||
assert_eq!(test_tx_bytes, serialized_tx_to_bytes);
|
||||
}
|
||||
|
||||
// Verify that bdk-ffi Address.payload includes expected WitnessProgram variant, version and program bytes.
|
||||
#[test]
|
||||
fn test_address_witness_program() {
|
||||
let address =
|
||||
Address::new("bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv".to_string()).unwrap();
|
||||
let payload = address.payload();
|
||||
assert_matches!(payload, Payload::WitnessProgram { version, program } => {
|
||||
assert_eq!(version,WitnessVersion::V0);
|
||||
assert_eq!(program, Vec::from_hex("04a6545885dd87b8e147cd327f2e9db362b72346").unwrap());
|
||||
});
|
||||
assert_eq!(address.network(), Regtest);
|
||||
}
|
||||
}
|
||||
// /// The method used to produce an address.
|
||||
// #[derive(Debug)]
|
||||
// pub enum Payload {
|
||||
// /// P2PKH address.
|
||||
// PubkeyHash { pubkey_hash: Vec<u8> },
|
||||
// /// P2SH address.
|
||||
// ScriptHash { script_hash: Vec<u8> },
|
||||
// /// Segwit address.
|
||||
// WitnessProgram {
|
||||
// /// The witness program version.
|
||||
// version: WitnessVersion,
|
||||
// /// The witness program.
|
||||
// program: Vec<u8>,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// /// A Bitcoin script.
|
||||
// #[derive(Clone, Debug, PartialEq, Eq)]
|
||||
// pub struct Script {
|
||||
// inner: BdkScript,
|
||||
// }
|
||||
//
|
||||
// impl Script {
|
||||
// fn new(raw_output_script: Vec<u8>) -> Self {
|
||||
// let script: BdkScript = BdkScript::from(raw_output_script);
|
||||
// Script { inner: script }
|
||||
// }
|
||||
//
|
||||
// fn to_bytes(&self) -> Vec<u8> {
|
||||
// self.inner.to_bytes()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<BdkScript> for Script {
|
||||
// fn from(bdk_script: BdkScript) -> Self {
|
||||
// Script { inner: bdk_script }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[derive(Clone, Debug)]
|
||||
// enum RbfValue {
|
||||
// Default,
|
||||
// Value(u32),
|
||||
// }
|
||||
//
|
||||
// /// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
||||
// /// transaction details.
|
||||
// pub struct TxBuilderResult {
|
||||
// pub(crate) psbt: Arc<PartiallySignedTransaction>,
|
||||
// pub transaction_details: TransactionDetails,
|
||||
// }
|
||||
//
|
||||
// uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
|
||||
//
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use super::Transaction;
|
||||
// use crate::Network::Regtest;
|
||||
// use crate::{Address, Payload};
|
||||
// use assert_matches::assert_matches;
|
||||
// use bdk::bitcoin::hashes::hex::FromHex;
|
||||
// use bdk::bitcoin::util::address::WitnessVersion;
|
||||
//
|
||||
// // Verify that bdk-ffi Transaction can be created from valid bytes and serialized back into the same bytes.
|
||||
// #[test]
|
||||
// fn test_transaction_serde() {
|
||||
// let test_tx_bytes = Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700").unwrap();
|
||||
// let new_tx_from_bytes = Transaction::new(test_tx_bytes.clone()).unwrap();
|
||||
// let serialized_tx_to_bytes = new_tx_from_bytes.serialize();
|
||||
// assert_eq!(test_tx_bytes, serialized_tx_to_bytes);
|
||||
// }
|
||||
//
|
||||
// // Verify that bdk-ffi Address.payload includes expected WitnessProgram variant, version and program bytes.
|
||||
// #[test]
|
||||
// fn test_address_witness_program() {
|
||||
// let address =
|
||||
// Address::new("bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv".to_string()).unwrap();
|
||||
// let payload = address.payload();
|
||||
// assert_matches!(payload, Payload::WitnessProgram { version, program } => {
|
||||
// assert_eq!(version,WitnessVersion::V0);
|
||||
// assert_eq!(program, Vec::from_hex("04a6545885dd87b8e147cd327f2e9db362b72346").unwrap());
|
||||
// });
|
||||
// assert_eq!(address.network(), Regtest);
|
||||
// }
|
||||
// }
|
||||
|
@ -1,119 +1,119 @@
|
||||
use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||
use bdk::bitcoincore_rpc::jsonrpc::serde_json;
|
||||
use bdk::psbt::PsbtUtils;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{BdkError, FeeRate, Transaction};
|
||||
|
||||
#[derive(Debug)]
|
||||
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)?;
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::wallet::{TxBuilder, Wallet};
|
||||
use bdk::wallet::get_funded_wallet;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[test]
|
||||
fn test_psbt_fee() {
|
||||
let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||
let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||
let test_wallet = Wallet {
|
||||
inner_mutex: Mutex::new(funded_wallet),
|
||||
};
|
||||
let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
||||
let drain_to_script = crate::Address::new(drain_to_address)
|
||||
.unwrap()
|
||||
.script_pubkey();
|
||||
|
||||
let tx_builder = TxBuilder::new()
|
||||
.fee_rate(2.0)
|
||||
.drain_wallet()
|
||||
.drain_to(drain_to_script.clone());
|
||||
//dbg!(&tx_builder);
|
||||
assert!(tx_builder.drain_wallet);
|
||||
assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
// use bdk::bitcoin::hashes::hex::ToHex;
|
||||
// use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||
// use bdk::bitcoincore_rpc::jsonrpc::serde_json;
|
||||
// use bdk::psbt::PsbtUtils;
|
||||
// use std::ops::Deref;
|
||||
// use std::str::FromStr;
|
||||
// use std::sync::{Arc, Mutex};
|
||||
//
|
||||
// use crate::{BdkError, FeeRate, Transaction};
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// 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)?;
|
||||
// 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()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use crate::wallet::{TxBuilder, Wallet};
|
||||
// use bdk::wallet::get_funded_wallet;
|
||||
// use std::sync::Mutex;
|
||||
//
|
||||
// #[test]
|
||||
// fn test_psbt_fee() {
|
||||
// let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||
// let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||
// let test_wallet = Wallet {
|
||||
// inner_mutex: Mutex::new(funded_wallet),
|
||||
// };
|
||||
// let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
||||
// let drain_to_script = crate::Address::new(drain_to_address)
|
||||
// .unwrap()
|
||||
// .script_pubkey();
|
||||
//
|
||||
// let tx_builder = TxBuilder::new()
|
||||
// .fee_rate(2.0)
|
||||
// .drain_wallet()
|
||||
// .drain_to(drain_to_script.clone());
|
||||
// //dbg!(&tx_builder);
|
||||
// assert!(tx_builder.drain_wallet);
|
||||
// assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
|
||||
//
|
||||
// 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);
|
||||
// }
|
||||
// }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,67 +7,43 @@ import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Library test, which will execute on linux host.
|
||||
*/
|
||||
class JvmLibTest {
|
||||
|
||||
private fun getTestDataDir(): String {
|
||||
return Files.createTempDirectory("bdk-test").toString()
|
||||
}
|
||||
|
||||
private fun cleanupTestDataDir(testDataDir: String) {
|
||||
File(testDataDir).deleteRecursively()
|
||||
}
|
||||
|
||||
class LogProgress : Progress {
|
||||
private val log: Logger = LoggerFactory.getLogger(JvmLibTest::class.java)
|
||||
|
||||
override fun update(progress: Float, message: String?) {
|
||||
log.debug("Syncing...")
|
||||
}
|
||||
}
|
||||
|
||||
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
|
||||
private val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
100u,
|
||||
true,
|
||||
)
|
||||
)
|
||||
|
||||
class WalletTest {
|
||||
@Test
|
||||
fun memoryWalletNewAddress() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val address = wallet.getAddress(AddressIndex.New).address.asString()
|
||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
||||
fun testNetwork() {
|
||||
val signetNetwork = Network.SIGNET
|
||||
}
|
||||
|
||||
@Test
|
||||
fun memoryWalletSyncGetBalance() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
val descriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sqliteWalletSyncGetBalance() {
|
||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
cleanupTestDataDir(testDataDir)
|
||||
fun testUsedWallet() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val (index, address, keychain) = wallet.getAddress(AddressIndex.LastUnused)
|
||||
println("Address ${address.asString()} at index $index")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
|
||||
assert(wallet.getBalance().total() == 0uL)
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun testSyncedBalance() {
|
||||
// val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
// val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET, WalletType.MEMORY)
|
||||
// val esploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
// // val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
|
||||
// val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
// wallet.applyUpdate(update)
|
||||
// println("Balance: ${wallet.getBalance().total()}")
|
||||
// }
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
// TODO 18: Migrate hard coded strings to constants all in the same location so they're at least easy
|
||||
// to find and reason about.
|
||||
internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = target.run {
|
||||
|
||||
@ -95,7 +97,7 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
doFirst {
|
||||
copy {
|
||||
with(it) {
|
||||
from("${project.projectDir}/../../target/${this.targetDir}/release-smaller/${libName}.${this.ext}")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/${this.targetDir}/release-smaller/${libName}.${this.ext}")
|
||||
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
|
||||
}
|
||||
}
|
||||
@ -108,9 +110,22 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
|
||||
dependsOn(moveNativeJvmLibs)
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin", "--no-format")
|
||||
// TODO 2: Is the Windows name the correct one?
|
||||
// TODO 3: This will not work on mac Intel (x86_64 architecture)
|
||||
// val libraryPath = when (operatingSystem) {
|
||||
// OS.LINUX -> "./target/x86_64-unknown-linux-gnu/release-smaller/libbdkffi.so"
|
||||
// OS.MAC -> "./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib"
|
||||
// OS.WINDOWS -> "./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll"
|
||||
// else -> throw Exception("Unsupported OS")
|
||||
// }
|
||||
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin/", "--no-format")
|
||||
|
||||
// The code above was for the migration to uniffi 0.24.3 using the --library flag
|
||||
// The code below works with uniffi 0.23.0
|
||||
workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin", "--no-format")
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
|
@ -13,6 +13,6 @@ rustup default 1.67.0
|
||||
cargo build --profile release-smaller
|
||||
|
||||
echo "Copying linux libbdkffi.so..."
|
||||
cp ../target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so
|
||||
cp ./target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so
|
||||
|
||||
echo "All done!"
|
||||
|
@ -14,6 +14,6 @@ rustup target add aarch64-apple-darwin
|
||||
cargo build --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
echo "Copying libraries libbdkffi.dylib..."
|
||||
cp ../target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
cp ./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
|
||||
echo "All done!"
|
||||
|
@ -9,10 +9,11 @@ cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.67.0
|
||||
rustup target add x86_64-apple-darwin
|
||||
cargo build --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
echo "Copying libraries libbdkffi.dylib..."
|
||||
cp ../target/x86_64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
cp ./target/x86_64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
|
||||
echo "All done!"
|
||||
|
@ -14,6 +14,6 @@ rustup target add x86_64-pc-windows-msvc
|
||||
cargo build --profile release-smaller --target x86_64-pc-windows-msvc
|
||||
|
||||
echo "Copying libraries bdkffi.dll..."
|
||||
cp ../target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll ../bdk-python/src/bdkpython/bdkffi.dll
|
||||
cp ./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll ../bdk-python/src/bdkpython/bdkffi.dll
|
||||
|
||||
echo "All done!"
|
||||
|
@ -3,61 +3,19 @@ import unittest
|
||||
|
||||
|
||||
descriptor = bdk.Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", bdk.Network.TESTNET)
|
||||
db_config = bdk.DatabaseConfig.MEMORY()
|
||||
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
|
||||
bdk.ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
None,
|
||||
5,
|
||||
None,
|
||||
100,
|
||||
True,
|
||||
)
|
||||
)
|
||||
blockchain = bdk.Blockchain(blockchain_config)
|
||||
|
||||
|
||||
class TestSimpleBip84Wallet(unittest.TestCase):
|
||||
|
||||
def test_address_bip84_testnet(self):
|
||||
wallet = bdk.Wallet(
|
||||
wallet = bdk.Wallet.new_no_persist(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config
|
||||
)
|
||||
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
|
||||
address = address_info.address.as_string()
|
||||
# print(f"New address is {address}")
|
||||
assert address == "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", f"Wrong address {address}, should be tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
|
||||
|
||||
def test_wallet_balance(self):
|
||||
wallet = bdk.Wallet(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config,
|
||||
)
|
||||
wallet.sync(blockchain, None)
|
||||
balance = wallet.get_balance()
|
||||
# print(f"Balance is {balance.total} sat")
|
||||
assert balance.total > 0, "Balance is 0, send testnet coins to tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
|
||||
|
||||
def test_output_address_from_script_pubkey(self):
|
||||
wallet = bdk.Wallet(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config,
|
||||
)
|
||||
wallet.sync(blockchain, None)
|
||||
first_tx = list(wallet.list_transactions(True))[0]
|
||||
assert first_tx.txid == '35d3de8dd429ec4c9684168c1fbb9a4fb6db6f2ce89be214a024657a73ef4908'
|
||||
|
||||
output1, output2 = list(first_tx.transaction.output())
|
||||
|
||||
assert bdk.Address.from_script(output1.script_pubkey, bdk.Network.TESTNET).as_string() == 'tb1qw6ly2te8k9vy2mwj3g6gx82hj7hc8f5q3vry8t'
|
||||
assert bdk.Address.from_script(output2.script_pubkey, bdk.Network.TESTNET).as_string() == 'tb1qzsvpnmme78yl60j7ldh9aqvhvxr4mz7mjpmh22'
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -7,9 +7,27 @@ final class BitcoinDevKitTests: XCTestCase {
|
||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
network: Network.regtest
|
||||
)
|
||||
let databaseConfig = DatabaseConfig.memory
|
||||
let wallet = try Wallet.init(descriptor: desc, changeDescriptor: nil, network: Network.regtest, databaseConfig: databaseConfig)
|
||||
let addressInfo = try wallet.getAddress(addressIndex: AddressIndex.new)
|
||||
XCTAssertEqual(addressInfo.address.asString(), "bcrt1qzg4mckdh50nwdm9hkzq06528rsu73hjxytqkxs")
|
||||
let wallet = try Wallet.newNoPersist(descriptor: desc, changeDescriptor: nil, network: .testnet)
|
||||
let addressInfo = wallet.getAddress(addressIndex: AddressIndex.lastUnused)
|
||||
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
|
||||
}
|
||||
|
||||
// func testConnectedWalletBalance() throws {
|
||||
// let descriptor = try Descriptor(
|
||||
// descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
// network: Network.testnet
|
||||
// )
|
||||
// let wallet = try Wallet.newNoPersist(
|
||||
// descriptor: descriptor,
|
||||
// changeDescriptor: nil,
|
||||
// network: .testnet
|
||||
// )
|
||||
//
|
||||
// let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
|
||||
// // val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
|
||||
// let update = try esploraClient.scan(wallet: wallet, stopGap: 10, parallelRequests: 1)
|
||||
// try wallet.applyUpdate(update: update)
|
||||
//
|
||||
// print("Balance: \(wallet.getBalance().total())")
|
||||
// }
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
# This script builds local swift-bdk Swift language bindings and corresponding bdkFFI.xcframework.
|
||||
# The results of this script can be used for locally testing your SPM package adding a local package
|
||||
# to your application pointing at the bdk-swift directory.
|
||||
#
|
||||
|
||||
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
|
||||
|
||||
rustup install nightly-2023-04-10
|
||||
@ -14,7 +14,6 @@ rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
pushd bdk-ffi
|
||||
mkdir -p Sources/BitcoinDevKit
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
||||
popd
|
||||
|
||||
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
|
||||
@ -27,14 +26,15 @@ lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-appl
|
||||
mkdir -p target/lipo-macos/release-smaller
|
||||
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
||||
|
||||
popd
|
||||
pushd bdk-swift
|
||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||
cp ../target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
||||
cp ../target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
|
||||
cp ../target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
||||
#rm bdkFFI.xcframework.zip || true
|
||||
|
Loading…
x
Reference in New Issue
Block a user