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:
|
security_audit:
|
||||||
name: Security audit
|
name: Security audit
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: bdk-ffi
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: "Check out PR branch"
|
||||||
- uses: actions-rs/audit-check@v1
|
uses: actions/checkout@v3
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
- 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:
|
jobs:
|
||||||
build-test:
|
build-test:
|
||||||
name: Build and test
|
name: "Build and test"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: bdk-ffi
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- version: 1.71.0 # STABLE
|
- version: 1.67.0
|
||||||
clippy: true
|
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:
|
steps:
|
||||||
- name: Checkout
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Generate cache key
|
- name: "Generate cache key"
|
||||||
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||||
|
|
||||||
- name: Cache
|
- name: "Cache"
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@ -33,53 +37,56 @@ jobs:
|
|||||||
target
|
target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
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 }}
|
run: rustup default ${{ matrix.rust.version }}
|
||||||
|
|
||||||
- name: Set profile
|
- name: "Set profile"
|
||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
|
|
||||||
- name: Add clippy
|
- name: "Add clippy"
|
||||||
if: ${{ matrix.rust.clippy }}
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: rustup component add clippy
|
run: rustup component add clippy
|
||||||
|
|
||||||
- name: Update toolchain
|
- name: "Update toolchain"
|
||||||
run: rustup update
|
run: rustup update
|
||||||
|
|
||||||
- name: Pin dependencies for MSRV
|
- name: "Pin dependencies for MSRV"
|
||||||
if: matrix.rust.version == '1.61.0'
|
if: matrix.rust.version == '1.61.0'
|
||||||
run: |
|
run: |
|
||||||
cargo update -p hashlink --precise "0.8.1"
|
cargo update -p hashlink --precise "0.8.1"
|
||||||
cargo update -p tokio --precise "1.29.1"
|
cargo update -p tokio --precise "1.29.1"
|
||||||
cargo update -p flate2 --precise "1.0.26"
|
cargo update -p flate2 --precise "1.0.26"
|
||||||
- name: Build
|
- name: "Build"
|
||||||
run: cargo build
|
run: cargo build
|
||||||
|
|
||||||
- name: Clippy
|
- name: "Clippy"
|
||||||
if: ${{ matrix.rust.clippy }}
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
|
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
|
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test --features uniffi/bindgen-tests
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Rust fmt
|
name: "Rust fmt"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: bdk-ffi
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set default toolchain
|
- name: "Set default toolchain"
|
||||||
run: rustup default nightly
|
run: rustup default nightly
|
||||||
|
|
||||||
- name: Set profile
|
- name: "Set profile"
|
||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
|
|
||||||
- name: Add rustfmt
|
- name: "Add rustfmt"
|
||||||
run: rustup component add rustfmt
|
run: rustup component add rustfmt
|
||||||
|
|
||||||
- name: Update toolchain
|
- name: "Update toolchain"
|
||||||
run: rustup update
|
run: rustup update
|
||||||
|
|
||||||
- name: Check fmt
|
- name: "Check fmt"
|
||||||
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
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
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
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
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
39
.github/workflows/test-swift.yaml
vendored
39
.github/workflows/test-swift.yaml
vendored
@ -12,44 +12,15 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: "Build and test"
|
||||||
runs-on: macos-12
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: "Set default Rust version to 1.67.0"
|
- name: "Build Swift package"
|
||||||
run: rustup default 1.67.0
|
run: bash ./bdk-swift/build-local-swift.sh
|
||||||
|
|
||||||
- name: Install Rust targets
|
- name: "Run Swift tests"
|
||||||
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
|
|
||||||
working-directory: bdk-swift
|
working-directory: bdk-swift
|
||||||
run: swift test
|
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)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class AndroidLibTest {
|
class AndroidLibTest {
|
||||||
|
@Test
|
||||||
private fun getTestDataDir(): String {
|
fun testNetwork() {
|
||||||
val context = ApplicationProvider.getApplicationContext<Application>()
|
val signetNetwork = Network.SIGNET
|
||||||
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
|
@Test
|
||||||
fun memoryWalletSyncGetBalance() {
|
fun testDescriptorBip86() {
|
||||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
val mnemonic = Mnemonic(WordCount.WORDS12)
|
||||||
val blockchain = Blockchain(blockchainConfig)
|
val descriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||||
wallet.sync(blockchain, LogProgress())
|
val descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||||
val balance: Balance = wallet.getBalance()
|
|
||||||
assertTrue(balance.total > 0u)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun sqliteWalletSyncGetBalance() {
|
fun testUsedWallet() {
|
||||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
val (index, address, keychain) = wallet.getAddress(AddressIndex.LastUnused)
|
||||||
val blockchain = Blockchain(blockchainConfig)
|
println("Address ${address.asString()} at index $index")
|
||||||
wallet.sync(blockchain, LogProgress())
|
|
||||||
val balance: Balance = wallet.getBalance()
|
|
||||||
assertTrue(balance.total > 0u)
|
|
||||||
cleanupTestDataDir(testDataDir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||||
|
|
||||||
into("arm64-v8a") {
|
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") {
|
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") {
|
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 {
|
doLast {
|
||||||
@ -137,6 +137,12 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
||||||
dependsOn(moveNativeAndroidLibs)
|
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")
|
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")
|
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]
|
[package]
|
||||||
name = "bdk-ffi"
|
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>"]
|
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
@ -17,12 +17,30 @@ path = "uniffi-bindgen.rs"
|
|||||||
default = ["uniffi/cli"]
|
default = ["uniffi/cli"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdk = { version = "0.28.2", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
|
bdk = { version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
|
||||||
uniffi = { version = "0.23.0" }
|
|
||||||
|
# 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]
|
[build-dependencies]
|
||||||
uniffi = { version = "0.23.0", features = ["build"] }
|
uniffi = { version = "=0.23.0", features = ["build"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
uniffi = { version = "0.23.0", features = ["bindgen-tests"] }
|
uniffi = { version = "=0.23.0", features = ["bindgen-tests"] }
|
||||||
assert_matches = "1.5.0"
|
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]
|
[Error]
|
||||||
enum BdkError {
|
enum BdkError {
|
||||||
"InvalidU32Bytes",
|
|
||||||
"Generic",
|
"Generic",
|
||||||
"MissingCachedScripts",
|
|
||||||
"ScriptDoesntHaveAddressForm",
|
|
||||||
"NoRecipients",
|
"NoRecipients",
|
||||||
"NoUtxosSelected",
|
"NoUtxosSelected",
|
||||||
"OutputBelowDustLimit",
|
"OutputBelowDustLimit",
|
||||||
@ -27,25 +35,26 @@ enum BdkError {
|
|||||||
"SpendingPolicyRequired",
|
"SpendingPolicyRequired",
|
||||||
"InvalidPolicyPathError",
|
"InvalidPolicyPathError",
|
||||||
"Signer",
|
"Signer",
|
||||||
"InvalidNetwork",
|
|
||||||
"InvalidProgressValue",
|
|
||||||
"ProgressUpdateError",
|
|
||||||
"InvalidOutpoint",
|
"InvalidOutpoint",
|
||||||
"Descriptor",
|
"Descriptor",
|
||||||
"Encode",
|
|
||||||
"Miniscript",
|
"Miniscript",
|
||||||
"MiniscriptPsbt",
|
"MiniscriptPsbt",
|
||||||
"Bip32",
|
"Bip32",
|
||||||
"Secp256k1",
|
|
||||||
"Json",
|
|
||||||
"Hex",
|
|
||||||
"Psbt",
|
"Psbt",
|
||||||
"PsbtParse",
|
};
|
||||||
"Electrum",
|
|
||||||
"Esplora",
|
interface Balance {
|
||||||
"Sled",
|
u64 immature();
|
||||||
"Rusqlite",
|
|
||||||
"Rpc",
|
u64 trusted_pending();
|
||||||
|
|
||||||
|
u64 untrusted_pending();
|
||||||
|
|
||||||
|
u64 confirmed();
|
||||||
|
|
||||||
|
u64 trusted_spendable();
|
||||||
|
|
||||||
|
u64 total();
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary AddressInfo {
|
dictionary AddressInfo {
|
||||||
@ -59,9 +68,28 @@ interface AddressIndex {
|
|||||||
New();
|
New();
|
||||||
LastUnused();
|
LastUnused();
|
||||||
Peek(u32 index);
|
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 {
|
enum Network {
|
||||||
"Bitcoin",
|
"Bitcoin",
|
||||||
"Testnet",
|
"Testnet",
|
||||||
@ -69,45 +97,6 @@ enum Network {
|
|||||||
"Regtest",
|
"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 {
|
enum WordCount {
|
||||||
"Words12",
|
"Words12",
|
||||||
"Words15",
|
"Words15",
|
||||||
@ -116,260 +105,20 @@ enum WordCount {
|
|||||||
"Words24",
|
"Words24",
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary ElectrumConfig {
|
interface Address {
|
||||||
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 {
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
constructor(BlockchainConfig config);
|
constructor(string address, Network network);
|
||||||
|
|
||||||
[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);
|
|
||||||
|
|
||||||
Network network();
|
Network network();
|
||||||
|
|
||||||
[Throws=BdkError]
|
string to_qr_uri();
|
||||||
AddressInfo get_address(AddressIndex address_index);
|
|
||||||
|
|
||||||
[Throws=BdkError]
|
string as_string();
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FeeRate {
|
// ------------------------------------------------------------------------
|
||||||
[Name=from_sat_per_vb]
|
// bdk crate - descriptor module
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Mnemonic {
|
interface Mnemonic {
|
||||||
constructor(WordCount word_count);
|
constructor(WordCount word_count);
|
||||||
@ -453,55 +202,10 @@ interface Descriptor {
|
|||||||
string as_string_private();
|
string as_string_private();
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Address {
|
// ------------------------------------------------------------------------
|
||||||
[Throws=BdkError]
|
// bdk_esplora crate
|
||||||
constructor(string address);
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
[Name=from_script, Throws=BdkError]
|
interface EsploraClient {
|
||||||
constructor(Script script, Network network);
|
constructor(string url);
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
};
|
||||||
|
@ -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::bip32::Fingerprint;
|
||||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
use bdk::bitcoin::key::Secp256k1;
|
||||||
use bdk::bitcoin::util::bip32::Fingerprint;
|
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||||
use bdk::bitcoin::Network;
|
use bdk::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
|
use bdk::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||||
use bdk::keys::{
|
|
||||||
DescriptorPublicKey as BdkDescriptorPublicKey, DescriptorSecretKey as BdkDescriptorSecretKey,
|
|
||||||
};
|
|
||||||
use bdk::template::{
|
use bdk::template::{
|
||||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
||||||
DescriptorTemplate,
|
DescriptorTemplate,
|
||||||
};
|
};
|
||||||
|
use bdk::Error as BdkError;
|
||||||
use bdk::KeychainKind;
|
use bdk::KeychainKind;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::keys::DescriptorPublicKey;
|
||||||
|
use crate::keys::DescriptorSecretKey;
|
||||||
|
use crate::Network;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Descriptor {
|
pub struct Descriptor {
|
||||||
pub(crate) extended_descriptor: ExtendedDescriptor,
|
pub extended_descriptor: ExtendedDescriptor,
|
||||||
pub(crate) key_map: KeyMap,
|
pub key_map: KeyMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Descriptor {
|
impl Descriptor {
|
||||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, BdkError> {
|
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, BdkError> {
|
||||||
let secp = Secp256k1::new();
|
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 {
|
Ok(Self {
|
||||||
extended_descriptor,
|
extended_descriptor,
|
||||||
key_map,
|
key_map,
|
||||||
@ -38,16 +41,20 @@ impl Descriptor {
|
|||||||
let derivable_key = &secret_key.inner;
|
let derivable_key = &secret_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) = Bip44(derivable_key, keychain_kind)
|
||||||
Bip44(derivable_key, keychain_kind).build(network).unwrap();
|
.build(network.into())
|
||||||
|
.unwrap();
|
||||||
Self {
|
Self {
|
||||||
extended_descriptor,
|
extended_descriptor,
|
||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => {
|
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,11 +70,14 @@ impl Descriptor {
|
|||||||
let derivable_key = &public_key.inner;
|
let derivable_key = &public_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorPublicKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) =
|
||||||
Bip44Public(derivable_key, fingerprint, keychain_kind)
|
Bip44Public(derivable_key, fingerprint, keychain_kind)
|
||||||
.build(network)
|
.build(network.into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -75,7 +85,7 @@ impl Descriptor {
|
|||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::Single(_) => {
|
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,16 +99,20 @@ impl Descriptor {
|
|||||||
let derivable_key = &secret_key.inner;
|
let derivable_key = &secret_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) = Bip49(derivable_key, keychain_kind)
|
||||||
Bip49(derivable_key, keychain_kind).build(network).unwrap();
|
.build(network.into())
|
||||||
|
.unwrap();
|
||||||
Self {
|
Self {
|
||||||
extended_descriptor,
|
extended_descriptor,
|
||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => {
|
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,11 +128,14 @@ impl Descriptor {
|
|||||||
let derivable_key = &public_key.inner;
|
let derivable_key = &public_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorPublicKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) =
|
||||||
Bip49Public(derivable_key, fingerprint, keychain_kind)
|
Bip49Public(derivable_key, fingerprint, keychain_kind)
|
||||||
.build(network)
|
.build(network.into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -126,7 +143,7 @@ impl Descriptor {
|
|||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::Single(_) => {
|
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,16 +157,20 @@ impl Descriptor {
|
|||||||
let derivable_key = &secret_key.inner;
|
let derivable_key = &secret_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) = Bip84(derivable_key, keychain_kind)
|
||||||
Bip84(derivable_key, keychain_kind).build(network).unwrap();
|
.build(network.into())
|
||||||
|
.unwrap();
|
||||||
Self {
|
Self {
|
||||||
extended_descriptor,
|
extended_descriptor,
|
||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => {
|
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,11 +186,14 @@ impl Descriptor {
|
|||||||
let derivable_key = &public_key.inner;
|
let derivable_key = &public_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorPublicKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) =
|
||||||
Bip84Public(derivable_key, fingerprint, keychain_kind)
|
Bip84Public(derivable_key, fingerprint, keychain_kind)
|
||||||
.build(network)
|
.build(network.into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -177,7 +201,7 @@ impl Descriptor {
|
|||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::Single(_) => {
|
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,16 +215,20 @@ impl Descriptor {
|
|||||||
let derivable_key = &secret_key.inner;
|
let derivable_key = &secret_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) = Bip86(derivable_key, keychain_kind)
|
||||||
Bip86(derivable_key, keychain_kind).build(network).unwrap();
|
.build(network.into())
|
||||||
|
.unwrap();
|
||||||
Self {
|
Self {
|
||||||
extended_descriptor,
|
extended_descriptor,
|
||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => {
|
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,11 +244,14 @@ impl Descriptor {
|
|||||||
let derivable_key = &public_key.inner;
|
let derivable_key = &public_key.inner;
|
||||||
|
|
||||||
match derivable_key {
|
match derivable_key {
|
||||||
|
BdkDescriptorPublicKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||||
let derivable_key = descriptor_x_key.xkey;
|
let derivable_key = descriptor_x_key.xkey;
|
||||||
let (extended_descriptor, key_map, _) =
|
let (extended_descriptor, key_map, _) =
|
||||||
Bip86Public(derivable_key, fingerprint, keychain_kind)
|
Bip86Public(derivable_key, fingerprint, keychain_kind)
|
||||||
.build(network)
|
.build(network.into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -228,7 +259,7 @@ impl Descriptor {
|
|||||||
key_map,
|
key_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::Single(_) => {
|
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,12 +276,11 @@ impl Descriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
// // 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`
|
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||||
// crate.
|
// // crate.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::database::DatabaseConfig;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use bdk::descriptor::DescriptorError::Key;
|
use bdk::descriptor::DescriptorError::Key;
|
||||||
@ -385,26 +415,4 @@ mod test {
|
|||||||
bdk::Error::Descriptor(Key(InvalidNetwork))
|
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::bip32::DerivationPath as BdkDerivationPath;
|
||||||
|
use bdk::bitcoin::key::Secp256k1;
|
||||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
use bdk::bitcoin::secp256k1::rand;
|
||||||
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
use bdk::bitcoin::secp256k1::rand::Rng;
|
||||||
use bdk::bitcoin::Network;
|
use bdk::keys::bip39::WordCount;
|
||||||
use bdk::descriptor::DescriptorXKey;
|
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
|
|
||||||
use bdk::keys::{
|
use bdk::keys::{
|
||||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||||
};
|
};
|
||||||
|
use bdk::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
||||||
use bdk::miniscript::BareCtx;
|
use bdk::miniscript::BareCtx;
|
||||||
|
use bdk::Error as BdkError;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
/// Mnemonic phrases are a human-readable version of the private keys.
|
use crate::Network;
|
||||||
/// Supported number of words are 12, 15, 18, 21 and 24.
|
|
||||||
|
// /// Mnemonic phrases are a human-readable version of the private keys.
|
||||||
|
// /// Supported number of words are 12, 15, 18, 21 and 24.
|
||||||
pub(crate) struct Mnemonic {
|
pub(crate) struct Mnemonic {
|
||||||
inner: BdkMnemonic,
|
inner: BdkMnemonic,
|
||||||
}
|
}
|
||||||
@ -23,8 +26,13 @@ pub(crate) struct Mnemonic {
|
|||||||
impl Mnemonic {
|
impl Mnemonic {
|
||||||
/// Generates Mnemonic with a random entropy
|
/// Generates Mnemonic with a random entropy
|
||||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
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> =
|
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();
|
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||||
Mnemonic { inner: mnemonic }
|
Mnemonic { inner: mnemonic }
|
||||||
}
|
}
|
||||||
@ -65,7 +73,7 @@ impl DerivationPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct DescriptorSecretKey {
|
pub struct DescriptorSecretKey {
|
||||||
pub(crate) inner: BdkDescriptorSecretKey,
|
pub(crate) inner: BdkDescriptorSecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,9 +83,9 @@ impl DescriptorSecretKey {
|
|||||||
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
||||||
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||||
origin: None,
|
origin: None,
|
||||||
xkey: xkey.into_xprv(network).unwrap(),
|
xkey: xkey.into_xprv(network.into()).unwrap(),
|
||||||
derivation_path: BdkDerivationPath::master(),
|
derivation_path: BdkDerivationPath::master(),
|
||||||
wildcard: bdk::descriptor::Wildcard::Unhardened,
|
wildcard: Wildcard::Unhardened,
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
inner: descriptor_secret_key,
|
inner: descriptor_secret_key,
|
||||||
@ -97,6 +105,9 @@ impl DescriptorSecretKey {
|
|||||||
let descriptor_secret_key = &self.inner;
|
let descriptor_secret_key = &self.inner;
|
||||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||||
match descriptor_secret_key {
|
match descriptor_secret_key {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||||
|
"Cannot derive from a single key".to_string(),
|
||||||
|
)),
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?;
|
let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?;
|
||||||
let key_source = match descriptor_x_key.origin.clone() {
|
let key_source = match descriptor_x_key.origin.clone() {
|
||||||
@ -113,8 +124,8 @@ impl DescriptorSecretKey {
|
|||||||
inner: derived_descriptor_secret_key,
|
inner: derived_descriptor_secret_key,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
BdkDescriptorSecretKey::MultiXPrv(_) => Err(BdkError::Generic(
|
||||||
"Cannot derive from a single key".to_string(),
|
"Cannot derive from a multi key".to_string(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,6 +134,9 @@ impl DescriptorSecretKey {
|
|||||||
let descriptor_secret_key = &self.inner;
|
let descriptor_secret_key = &self.inner;
|
||||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||||
match descriptor_secret_key {
|
match descriptor_secret_key {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||||
|
"Cannot extend from a single key".to_string(),
|
||||||
|
)),
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||||
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||||
@ -135,8 +149,8 @@ impl DescriptorSecretKey {
|
|||||||
inner: extended_descriptor_secret_key,
|
inner: extended_descriptor_secret_key,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
BdkDescriptorSecretKey::MultiXPrv(_) => Err(BdkError::Generic(
|
||||||
"Cannot extend from a single key".to_string(),
|
"Cannot derive from a multi key".to_string(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,10 +167,13 @@ impl DescriptorSecretKey {
|
|||||||
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
||||||
let inner = &self.inner;
|
let inner = &self.inner;
|
||||||
let secret_bytes: Vec<u8> = match inner.deref() {
|
let secret_bytes: Vec<u8> = match inner.deref() {
|
||||||
|
BdkDescriptorSecretKey::Single(_) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||||
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||||
}
|
}
|
||||||
BdkDescriptorSecretKey::Single(_) => {
|
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -170,7 +187,7 @@ impl DescriptorSecretKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct DescriptorPublicKey {
|
pub struct DescriptorPublicKey {
|
||||||
pub(crate) inner: BdkDescriptorPublicKey,
|
pub(crate) inner: BdkDescriptorPublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +206,9 @@ impl DescriptorPublicKey {
|
|||||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||||
|
|
||||||
match descriptor_public_key.deref() {
|
match descriptor_public_key.deref() {
|
||||||
|
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||||
|
"Cannot derive from a single key".to_string(),
|
||||||
|
)),
|
||||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||||
let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?;
|
let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?;
|
||||||
let key_source = match descriptor_x_key.origin.clone() {
|
let key_source = match descriptor_x_key.origin.clone() {
|
||||||
@ -205,8 +225,8 @@ impl DescriptorPublicKey {
|
|||||||
inner: derived_descriptor_public_key,
|
inner: derived_descriptor_public_key,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
|
||||||
"Cannot derive from a single key".to_string(),
|
"Cannot derive from a multi xpub".to_string(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +235,9 @@ impl DescriptorPublicKey {
|
|||||||
let descriptor_public_key = &self.inner;
|
let descriptor_public_key = &self.inner;
|
||||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||||
match descriptor_public_key.deref() {
|
match descriptor_public_key.deref() {
|
||||||
|
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||||
|
"Cannot extend from a single key".to_string(),
|
||||||
|
)),
|
||||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||||
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||||
@ -227,8 +250,8 @@ impl DescriptorPublicKey {
|
|||||||
inner: extended_descriptor_public_key,
|
inner: extended_descriptor_public_key,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
|
||||||
"Cannot extend from a single key".to_string(),
|
"Cannot derive from a multi xpub".to_string(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,21 +260,21 @@ impl DescriptorPublicKey {
|
|||||||
self.inner.to_string()
|
self.inner.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//
|
||||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
// // 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`
|
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||||
// crate.
|
// // crate.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||||
use crate::BdkError;
|
use crate::BdkError;
|
||||||
use bdk::bitcoin::hashes::hex::ToHex;
|
// use bdk::bitcoin::hashes::hex::ToHex;
|
||||||
use bdk::bitcoin::Network;
|
use bdk::bitcoin::Network;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn get_inner() -> DescriptorSecretKey {
|
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();
|
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(
|
fn derive_dsk(
|
||||||
@ -359,13 +382,16 @@ mod test {
|
|||||||
assert!(derived_dpk.is_err());
|
assert!(derived_dpk.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// TODO 7: It appears that the to_hex() method is not available anymore.
|
||||||
fn test_retrieve_master_secret_key() {
|
// Look into the correct way to pull the hex out of the DescriptorSecretKey.
|
||||||
let master_dpk = get_inner();
|
// Note: ToHex was removed in bitcoin_hashes 0.12.0
|
||||||
let master_private_key = master_dpk.secret_bytes().to_hex();
|
// #[test]
|
||||||
assert_eq!(
|
// fn test_retrieve_master_secret_key() {
|
||||||
master_private_key,
|
// let master_dpk = get_inner();
|
||||||
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
// 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 descriptor;
|
||||||
|
mod esplora;
|
||||||
mod keys;
|
mod keys;
|
||||||
mod psbt;
|
mod psbt;
|
||||||
mod wallet;
|
mod wallet;
|
||||||
|
|
||||||
use crate::blockchain::{
|
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||||
Auth, Blockchain, BlockchainConfig, ElectrumConfig, EsploraConfig, RpcConfig, RpcSyncParams,
|
use bdk::bitcoin::Address as BdkAddress;
|
||||||
};
|
use bdk::bitcoin::Network as BdkNetwork;
|
||||||
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::wallet::AddressIndex as BdkAddressIndex;
|
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
||||||
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
||||||
use bdk::LocalUtxo as BdkLocalUtxo;
|
use bdk::wallet::Balance as BdkBalance;
|
||||||
use bdk::TransactionDetails as BdkTransactionDetails;
|
use bdk::Error as BdkError;
|
||||||
use bdk::{Balance as BdkBalance, BlockTime, Error as BdkError, FeeRate, KeychainKind};
|
use bdk::KeychainKind;
|
||||||
use std::convert::From;
|
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::Arc;
|
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");
|
uniffi::include_scaffolding!("bdk");
|
||||||
|
|
||||||
/// A output script and an amount of satoshis.
|
pub enum Network {
|
||||||
pub struct ScriptAmount {
|
/// Mainnet Bitcoin.
|
||||||
pub script: Arc<Script>,
|
Bitcoin,
|
||||||
pub amount: u64,
|
/// 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.
|
/// A derived address and the index it was found at.
|
||||||
pub struct AddressInfo {
|
pub struct AddressInfo {
|
||||||
/// Child index of this address.
|
/// Child index of this address.
|
||||||
@ -61,7 +82,7 @@ impl From<BdkAddressInfo> for AddressInfo {
|
|||||||
fn from(address_info: BdkAddressInfo) -> Self {
|
fn from(address_info: BdkAddressInfo) -> Self {
|
||||||
AddressInfo {
|
AddressInfo {
|
||||||
index: address_info.index,
|
index: address_info.index,
|
||||||
address: Arc::new(Address::from(address_info.address)),
|
address: Arc::new(address_info.address.into()),
|
||||||
keychain: address_info.keychain,
|
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
|
/// 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.
|
/// then the returned address may have already been used.
|
||||||
Peek { index: u32 },
|
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 {
|
impl From<AddressIndex> for BdkAddressIndex {
|
||||||
@ -100,301 +113,350 @@ impl From<AddressIndex> for BdkAddressIndex {
|
|||||||
AddressIndex::New => BdkAddressIndex::New,
|
AddressIndex::New => BdkAddressIndex::New,
|
||||||
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||||
AddressIndex::Peek { index } => BdkAddressIndex::Peek(index),
|
AddressIndex::Peek { index } => BdkAddressIndex::Peek(index),
|
||||||
AddressIndex::Reset { index } => BdkAddressIndex::Reset(index),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wallet transaction
|
// TODO 9: Peek is not correctly implemented
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
impl From<&AddressIndex> for BdkAddressIndex {
|
||||||
pub struct TransactionDetails {
|
fn from(address_index: &AddressIndex) -> Self {
|
||||||
pub transaction: Option<Arc<Transaction>>,
|
match address_index {
|
||||||
/// Transaction id.
|
AddressIndex::New => BdkAddressIndex::New,
|
||||||
pub txid: String,
|
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||||
/// Received value (sats)
|
AddressIndex::Peek { index } => BdkAddressIndex::Peek(*index),
|
||||||
/// 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.
|
impl From<BdkAddressIndex> for AddressIndex {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
fn from(address_index: BdkAddressIndex) -> Self {
|
||||||
pub struct OutPoint {
|
match address_index {
|
||||||
/// The referenced transaction's txid.
|
BdkAddressIndex::New => AddressIndex::New,
|
||||||
txid: String,
|
BdkAddressIndex::LastUnused => AddressIndex::LastUnused,
|
||||||
/// The index of the referenced output in its transaction's vout.
|
_ => panic!("Mmmm not working"),
|
||||||
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// 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 {
|
pub struct Balance {
|
||||||
// All coinbase outputs not yet matured
|
pub inner: BdkBalance,
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BdkBalance> for Balance {
|
impl Balance {
|
||||||
fn from(bdk_balance: BdkBalance) -> Self {
|
/// All coinbase outputs not yet matured.
|
||||||
Balance {
|
fn immature(&self) -> u64 {
|
||||||
immature: bdk_balance.immature,
|
self.inner.immature
|
||||||
trusted_pending: bdk_balance.trusted_pending,
|
}
|
||||||
untrusted_pending: bdk_balance.untrusted_pending,
|
|
||||||
confirmed: bdk_balance.confirmed,
|
/// Unconfirmed UTXOs generated by a wallet tx.
|
||||||
spendable: bdk_balance.get_spendable(),
|
fn trusted_pending(&self) -> u64 {
|
||||||
total: bdk_balance.get_total(),
|
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.
|
// impl From<BdkBalance> for Balance {
|
||||||
#[derive(Debug, Clone)]
|
// fn from(bdk_balance: BdkBalance) -> Self {
|
||||||
pub struct TxOut {
|
// Balance { inner: bdk_balance }
|
||||||
/// The value of the output, in satoshis.
|
// }
|
||||||
value: u64,
|
// }
|
||||||
/// The address of the output.
|
|
||||||
script_pubkey: Arc<Script>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&BdkTxOut> for TxOut {
|
// /// A transaction output, which defines new coins to be created from old ones.
|
||||||
fn from(tx_out: &BdkTxOut) -> Self {
|
// #[derive(Debug, Clone)]
|
||||||
TxOut {
|
// pub struct TxOut {
|
||||||
value: tx_out.value,
|
// /// The value of the output, in satoshis.
|
||||||
script_pubkey: Arc::new(Script {
|
// value: u64,
|
||||||
inner: tx_out.script_pubkey.clone(),
|
// /// 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 {
|
// impl From<BdkTransaction> for Transaction {
|
||||||
outpoint: OutPoint,
|
// fn from(tx: BdkTransaction) -> Self {
|
||||||
txout: TxOut,
|
// Transaction { inner: tx }
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Bitcoin address.
|
/// A Bitcoin address.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
inner: BdkAddress,
|
inner: BdkAddress<NetworkChecked>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
fn new(address: String) -> Result<Self, BdkError> {
|
fn new(address: String, network: Network) -> Result<Self, BdkError> {
|
||||||
BdkAddress::from_str(address.as_str())
|
Ok(Address {
|
||||||
.map(|a| Address { inner: a })
|
inner: address
|
||||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
.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
|
/// alternative constructor
|
||||||
fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
|
// fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
|
||||||
BdkAddress::from_script(&script.inner, network)
|
// BdkAddress::from_script(&script.inner, network)
|
||||||
.map(|a| Address { inner: a })
|
// .map(|a| Address { inner: a })
|
||||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
// .map_err(|e| BdkError::Generic(e.to_string()))
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
fn payload(&self) -> Payload {
|
// fn payload(&self) -> Payload {
|
||||||
match &self.inner.payload.clone() {
|
// match &self.inner.payload.clone() {
|
||||||
BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
|
// BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
|
||||||
pubkey_hash: pubkey_hash.to_vec(),
|
// pubkey_hash: pubkey_hash.to_vec(),
|
||||||
},
|
// },
|
||||||
BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
|
// BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
|
||||||
script_hash: script_hash.to_vec(),
|
// script_hash: script_hash.to_vec(),
|
||||||
},
|
// },
|
||||||
BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
|
// BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
|
||||||
version: *version,
|
// version: *version,
|
||||||
program: program.clone(),
|
// program: program.clone(),
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn network(&self) -> Network {
|
fn network(&self) -> Network {
|
||||||
self.inner.network
|
self.inner.network.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn script_pubkey(&self) -> Arc<Script> {
|
// fn script_pubkey(&self) -> Arc<Script> {
|
||||||
Arc::new(Script {
|
// Arc::new(Script {
|
||||||
inner: self.inner.script_pubkey(),
|
// inner: self.inner.script_pubkey(),
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn to_qr_uri(&self) -> String {
|
fn to_qr_uri(&self) -> String {
|
||||||
self.inner.to_qr_uri()
|
self.inner.to_qr_uri()
|
||||||
@ -411,91 +473,97 @@ impl From<BdkAddress> for Address {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The method used to produce an address.
|
impl From<Address> for BdkAddress {
|
||||||
#[derive(Debug)]
|
fn from(address: Address) -> Self {
|
||||||
pub enum Payload {
|
address.inner
|
||||||
/// 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 {
|
// /// The method used to produce an address.
|
||||||
fn from(bdk_script: BdkScript) -> Self {
|
// #[derive(Debug)]
|
||||||
Script { inner: bdk_script }
|
// pub enum Payload {
|
||||||
}
|
// /// P2PKH address.
|
||||||
}
|
// PubkeyHash { pubkey_hash: Vec<u8> },
|
||||||
|
// /// P2SH address.
|
||||||
#[derive(Clone, Debug)]
|
// ScriptHash { script_hash: Vec<u8> },
|
||||||
enum RbfValue {
|
// /// Segwit address.
|
||||||
Default,
|
// WitnessProgram {
|
||||||
Value(u32),
|
// /// The witness program version.
|
||||||
}
|
// version: WitnessVersion,
|
||||||
|
// /// The witness program.
|
||||||
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
// program: Vec<u8>,
|
||||||
/// transaction details.
|
// },
|
||||||
pub struct TxBuilderResult {
|
// }
|
||||||
pub(crate) psbt: Arc<PartiallySignedTransaction>,
|
//
|
||||||
pub transaction_details: TransactionDetails,
|
// /// A Bitcoin script.
|
||||||
}
|
// #[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
// pub struct Script {
|
||||||
uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
|
// inner: BdkScript,
|
||||||
|
// }
|
||||||
// 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`
|
// impl Script {
|
||||||
// crate.
|
// fn new(raw_output_script: Vec<u8>) -> Self {
|
||||||
#[cfg(test)]
|
// let script: BdkScript = BdkScript::from(raw_output_script);
|
||||||
mod test {
|
// Script { inner: script }
|
||||||
use super::Transaction;
|
// }
|
||||||
use crate::Network::Regtest;
|
//
|
||||||
use crate::{Address, Payload};
|
// fn to_bytes(&self) -> Vec<u8> {
|
||||||
use assert_matches::assert_matches;
|
// self.inner.to_bytes()
|
||||||
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.
|
// impl From<BdkScript> for Script {
|
||||||
#[test]
|
// fn from(bdk_script: BdkScript) -> Self {
|
||||||
fn test_transaction_serde() {
|
// Script { inner: bdk_script }
|
||||||
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);
|
// #[derive(Clone, Debug)]
|
||||||
}
|
// enum RbfValue {
|
||||||
|
// Default,
|
||||||
// Verify that bdk-ffi Address.payload includes expected WitnessProgram variant, version and program bytes.
|
// Value(u32),
|
||||||
#[test]
|
// }
|
||||||
fn test_address_witness_program() {
|
//
|
||||||
let address =
|
// /// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
||||||
Address::new("bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv".to_string()).unwrap();
|
// /// transaction details.
|
||||||
let payload = address.payload();
|
// pub struct TxBuilderResult {
|
||||||
assert_matches!(payload, Payload::WitnessProgram { version, program } => {
|
// pub(crate) psbt: Arc<PartiallySignedTransaction>,
|
||||||
assert_eq!(version,WitnessVersion::V0);
|
// pub transaction_details: TransactionDetails,
|
||||||
assert_eq!(program, Vec::from_hex("04a6545885dd87b8e147cd327f2e9db362b72346").unwrap());
|
// }
|
||||||
});
|
//
|
||||||
assert_eq!(address.network(), Regtest);
|
// 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::hashes::hex::ToHex;
|
||||||
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
// use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||||
use bdk::bitcoincore_rpc::jsonrpc::serde_json;
|
// use bdk::bitcoincore_rpc::jsonrpc::serde_json;
|
||||||
use bdk::psbt::PsbtUtils;
|
// use bdk::psbt::PsbtUtils;
|
||||||
use std::ops::Deref;
|
// use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
// use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
// use std::sync::{Arc, Mutex};
|
||||||
|
//
|
||||||
use crate::{BdkError, FeeRate, Transaction};
|
// use crate::{BdkError, FeeRate, Transaction};
|
||||||
|
//
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub(crate) struct PartiallySignedTransaction {
|
// pub(crate) struct PartiallySignedTransaction {
|
||||||
pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
|
// pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl PartiallySignedTransaction {
|
// impl PartiallySignedTransaction {
|
||||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
// pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
||||||
let psbt: BdkPartiallySignedTransaction =
|
// let psbt: BdkPartiallySignedTransaction =
|
||||||
BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
|
// BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
|
||||||
Ok(PartiallySignedTransaction {
|
// Ok(PartiallySignedTransaction {
|
||||||
inner: Mutex::new(psbt),
|
// inner: Mutex::new(psbt),
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
pub(crate) fn serialize(&self) -> String {
|
// pub(crate) fn serialize(&self) -> String {
|
||||||
let psbt = self.inner.lock().unwrap().clone();
|
// let psbt = self.inner.lock().unwrap().clone();
|
||||||
psbt.to_string()
|
// psbt.to_string()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
pub(crate) fn txid(&self) -> String {
|
// pub(crate) fn txid(&self) -> String {
|
||||||
let tx = self.inner.lock().unwrap().clone().extract_tx();
|
// let tx = self.inner.lock().unwrap().clone().extract_tx();
|
||||||
let txid = tx.txid();
|
// let txid = tx.txid();
|
||||||
txid.to_hex()
|
// txid.to_hex()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/// Return the transaction.
|
// /// Return the transaction.
|
||||||
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
|
// pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
|
||||||
let tx = self.inner.lock().unwrap().clone().extract_tx();
|
// let tx = self.inner.lock().unwrap().clone().extract_tx();
|
||||||
Arc::new(tx.into())
|
// Arc::new(tx.into())
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
|
// /// 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)`
|
// /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||||
pub(crate) fn combine(
|
// pub(crate) fn combine(
|
||||||
&self,
|
// &self,
|
||||||
other: Arc<PartiallySignedTransaction>,
|
// other: Arc<PartiallySignedTransaction>,
|
||||||
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
// ) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||||
let other_psbt = other.inner.lock().unwrap().clone();
|
// let other_psbt = other.inner.lock().unwrap().clone();
|
||||||
let mut original_psbt = self.inner.lock().unwrap().clone();
|
// let mut original_psbt = self.inner.lock().unwrap().clone();
|
||||||
|
//
|
||||||
original_psbt.combine(other_psbt)?;
|
// original_psbt.combine(other_psbt)?;
|
||||||
Ok(Arc::new(PartiallySignedTransaction {
|
// Ok(Arc::new(PartiallySignedTransaction {
|
||||||
inner: Mutex::new(original_psbt),
|
// inner: Mutex::new(original_psbt),
|
||||||
}))
|
// }))
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
|
// /// 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.
|
// /// If the PSBT is missing a TxOut for an input returns None.
|
||||||
pub(crate) fn fee_amount(&self) -> Option<u64> {
|
// pub(crate) fn fee_amount(&self) -> Option<u64> {
|
||||||
self.inner.lock().unwrap().fee_amount()
|
// self.inner.lock().unwrap().fee_amount()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/// The transaction's fee rate. This value will only be accurate if calculated AFTER the
|
// /// 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
|
// /// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
|
||||||
/// transaction.
|
// /// transaction.
|
||||||
/// If the PSBT is missing a TxOut for an input returns None.
|
// /// If the PSBT is missing a TxOut for an input returns None.
|
||||||
pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
|
// pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
|
||||||
self.inner.lock().unwrap().fee_rate().map(Arc::new)
|
// self.inner.lock().unwrap().fee_rate().map(Arc::new)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/// Serialize the PSBT data structure as a String of JSON.
|
// /// Serialize the PSBT data structure as a String of JSON.
|
||||||
pub(crate) fn json_serialize(&self) -> String {
|
// pub(crate) fn json_serialize(&self) -> String {
|
||||||
let psbt = self.inner.lock().unwrap();
|
// let psbt = self.inner.lock().unwrap();
|
||||||
serde_json::to_string(psbt.deref()).unwrap()
|
// serde_json::to_string(psbt.deref()).unwrap()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
// // 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`
|
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||||
// crate.
|
// // crate.
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod test {
|
// mod test {
|
||||||
use crate::wallet::{TxBuilder, Wallet};
|
// use crate::wallet::{TxBuilder, Wallet};
|
||||||
use bdk::wallet::get_funded_wallet;
|
// use bdk::wallet::get_funded_wallet;
|
||||||
use std::sync::Mutex;
|
// use std::sync::Mutex;
|
||||||
|
//
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_psbt_fee() {
|
// fn test_psbt_fee() {
|
||||||
let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
// let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||||
let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
// let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||||
let test_wallet = Wallet {
|
// let test_wallet = Wallet {
|
||||||
inner_mutex: Mutex::new(funded_wallet),
|
// inner_mutex: Mutex::new(funded_wallet),
|
||||||
};
|
// };
|
||||||
let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
// let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
||||||
let drain_to_script = crate::Address::new(drain_to_address)
|
// let drain_to_script = crate::Address::new(drain_to_address)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.script_pubkey();
|
// .script_pubkey();
|
||||||
|
//
|
||||||
let tx_builder = TxBuilder::new()
|
// let tx_builder = TxBuilder::new()
|
||||||
.fee_rate(2.0)
|
// .fee_rate(2.0)
|
||||||
.drain_wallet()
|
// .drain_wallet()
|
||||||
.drain_to(drain_to_script.clone());
|
// .drain_to(drain_to_script.clone());
|
||||||
//dbg!(&tx_builder);
|
// //dbg!(&tx_builder);
|
||||||
assert!(tx_builder.drain_wallet);
|
// assert!(tx_builder.drain_wallet);
|
||||||
assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
|
// assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
|
||||||
|
//
|
||||||
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
// let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||||
|
//
|
||||||
assert!(tx_builder_result.psbt.fee_rate().is_some());
|
// assert!(tx_builder_result.psbt.fee_rate().is_some());
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
tx_builder_result.psbt.fee_rate().unwrap().as_sat_per_vb(),
|
// tx_builder_result.psbt.fee_rate().unwrap().as_sat_per_vb(),
|
||||||
2.682927
|
// 2.682927
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
assert!(tx_builder_result.psbt.fee_amount().is_some());
|
// assert!(tx_builder_result.psbt.fee_amount().is_some());
|
||||||
assert_eq!(tx_builder_result.psbt.fee_amount().unwrap(), 220);
|
// 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.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
/**
|
class WalletTest {
|
||||||
* 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,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun memoryWalletNewAddress() {
|
fun testNetwork() {
|
||||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
val signetNetwork = Network.SIGNET
|
||||||
val address = wallet.getAddress(AddressIndex.New).address.asString()
|
|
||||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun memoryWalletSyncGetBalance() {
|
fun testDescriptorBip86() {
|
||||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
val mnemonic = Mnemonic(WordCount.WORDS12)
|
||||||
val blockchain = Blockchain(blockchainConfig)
|
val descriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||||
wallet.sync(blockchain, LogProgress())
|
val descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||||
val balance: Balance = wallet.getBalance()
|
|
||||||
assertTrue(balance.total > 0u)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun sqliteWalletSyncGetBalance() {
|
fun testUsedWallet() {
|
||||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
val (index, address, keychain) = wallet.getAddress(AddressIndex.LastUnused)
|
||||||
val blockchain = Blockchain(blockchainConfig)
|
println("Address ${address.asString()} at index $index")
|
||||||
wallet.sync(blockchain, LogProgress())
|
|
||||||
val balance: Balance = wallet.getBalance()
|
|
||||||
assertTrue(balance.total > 0u)
|
|
||||||
cleanupTestDataDir(testDataDir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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.provideDelegate
|
||||||
import org.gradle.kotlin.dsl.register
|
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> {
|
internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||||
override fun apply(target: Project): Unit = target.run {
|
override fun apply(target: Project): Unit = target.run {
|
||||||
|
|
||||||
@ -95,7 +97,7 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
|||||||
doFirst {
|
doFirst {
|
||||||
copy {
|
copy {
|
||||||
with(it) {
|
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}/")
|
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,9 +110,22 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
dependsOn(moveNativeJvmLibs)
|
dependsOn(moveNativeJvmLibs)
|
||||||
|
|
||||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
// TODO 2: Is the Windows name the correct one?
|
||||||
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 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")
|
executable("cargo")
|
||||||
args(cargoArgs)
|
args(cargoArgs)
|
||||||
|
|
||||||
|
@ -13,6 +13,6 @@ rustup default 1.67.0
|
|||||||
cargo build --profile release-smaller
|
cargo build --profile release-smaller
|
||||||
|
|
||||||
echo "Copying linux libbdkffi.so..."
|
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!"
|
echo "All done!"
|
||||||
|
@ -14,6 +14,6 @@ rustup target add aarch64-apple-darwin
|
|||||||
cargo build --profile release-smaller --target aarch64-apple-darwin
|
cargo build --profile release-smaller --target aarch64-apple-darwin
|
||||||
|
|
||||||
echo "Copying libraries libbdkffi.dylib..."
|
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!"
|
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
|
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||||
|
|
||||||
echo "Generating native binaries..."
|
echo "Generating native binaries..."
|
||||||
|
rustup default 1.67.0
|
||||||
rustup target add x86_64-apple-darwin
|
rustup target add x86_64-apple-darwin
|
||||||
cargo build --profile release-smaller --target x86_64-apple-darwin
|
cargo build --profile release-smaller --target x86_64-apple-darwin
|
||||||
|
|
||||||
echo "Copying libraries libbdkffi.dylib..."
|
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!"
|
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
|
cargo build --profile release-smaller --target x86_64-pc-windows-msvc
|
||||||
|
|
||||||
echo "Copying libraries bdkffi.dll..."
|
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!"
|
echo "All done!"
|
||||||
|
@ -3,61 +3,19 @@ import unittest
|
|||||||
|
|
||||||
|
|
||||||
descriptor = bdk.Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", bdk.Network.TESTNET)
|
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):
|
class TestSimpleBip84Wallet(unittest.TestCase):
|
||||||
|
|
||||||
def test_address_bip84_testnet(self):
|
def test_address_bip84_testnet(self):
|
||||||
wallet = bdk.Wallet(
|
wallet = bdk.Wallet.new_no_persist(
|
||||||
descriptor=descriptor,
|
descriptor=descriptor,
|
||||||
change_descriptor=None,
|
change_descriptor=None,
|
||||||
network=bdk.Network.TESTNET,
|
network=bdk.Network.TESTNET,
|
||||||
database_config=db_config
|
|
||||||
)
|
)
|
||||||
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
|
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
|
||||||
address = address_info.address.as_string()
|
address = address_info.address.as_string()
|
||||||
# print(f"New address is {address}")
|
# print(f"New address is {address}")
|
||||||
assert address == "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", f"Wrong address {address}, should be tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -7,9 +7,27 @@ final class BitcoinDevKitTests: XCTestCase {
|
|||||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
network: Network.regtest
|
network: Network.regtest
|
||||||
)
|
)
|
||||||
let databaseConfig = DatabaseConfig.memory
|
let wallet = try Wallet.newNoPersist(descriptor: desc, changeDescriptor: nil, network: .testnet)
|
||||||
let wallet = try Wallet.init(descriptor: desc, changeDescriptor: nil, network: Network.regtest, databaseConfig: databaseConfig)
|
let addressInfo = wallet.getAddress(addressIndex: AddressIndex.lastUnused)
|
||||||
let addressInfo = try wallet.getAddress(addressIndex: AddressIndex.new)
|
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
|
||||||
XCTAssertEqual(addressInfo.address.asString(), "bcrt1qzg4mckdh50nwdm9hkzq06528rsu73hjxytqkxs")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
# 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
|
# 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.
|
# to your application pointing at the bdk-swift directory.
|
||||||
#
|
|
||||||
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
|
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
|
||||||
|
|
||||||
rustup install nightly-2023-04-10
|
rustup install nightly-2023-04-10
|
||||||
@ -14,7 +14,6 @@ rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
|||||||
pushd bdk-ffi
|
pushd bdk-ffi
|
||||||
mkdir -p Sources/BitcoinDevKit
|
mkdir -p Sources/BitcoinDevKit
|
||||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
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 x86_64-apple-darwin
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-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
|
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
|
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
|
pushd bdk-swift
|
||||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.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/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/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/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 ../bdk-ffi/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 ../bdk-ffi/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/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
||||||
#rm bdkFFI.xcframework.zip || true
|
#rm bdkFFI.xcframework.zip || true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user