Compare commits
57 Commits
release/1.
...
chore/bump
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5cf483223 | ||
|
|
c6174199dd | ||
|
|
9c45254c3e | ||
|
|
260a0a65b3 | ||
|
|
72985f14ad | ||
|
|
5e3e24906f | ||
|
|
c702894143 | ||
|
|
ecdd7c239b | ||
|
|
ca8a3d0471 | ||
|
|
8f4c80cb98 | ||
|
|
4aec4b0434 | ||
|
|
1913c45ef9 | ||
|
|
815fe5f62d | ||
|
|
8d30c86076 | ||
|
|
c88b33473b | ||
|
|
79e7ab73ea | ||
|
|
f169b1a52f | ||
|
|
97d9bb6fbf | ||
|
|
f27bada9c9 | ||
|
|
1b0b50a954 | ||
|
|
6e207802b2 | ||
|
|
ac15ed7380 | ||
|
|
e9a76287c8 | ||
|
|
72b5bfd4c9 | ||
|
|
19723240b7 | ||
|
|
7d951578d0 | ||
|
|
b7fe91b003 | ||
|
|
330dc96b8a | ||
|
|
5557bb94ea | ||
|
|
9b5b96710e | ||
|
|
75d155c67a | ||
|
|
6522dfdd26 | ||
|
|
ebaa6fda2f | ||
|
|
5e8271e158 | ||
|
|
431ab90f04 | ||
|
|
d4736a64d1 | ||
|
|
f31678bf37 | ||
|
|
8130a419f2 | ||
|
|
262704751c | ||
|
|
00a8e1ba8b | ||
|
|
d0514f678e | ||
|
|
f6cc63539d | ||
|
|
df64a96dd2 | ||
|
|
4cd6a80ce0 | ||
|
|
e48af63fe6 | ||
|
|
eff4abcbfb | ||
|
|
e1a93379ce | ||
|
|
e609b57bff | ||
|
|
282fcfce0a | ||
|
|
4dd4e91ccd | ||
|
|
4f8b7f4ba5 | ||
|
|
4d737d3393 | ||
|
|
e14124b454 | ||
|
|
0a75fc1279 | ||
|
|
6ac386c8df | ||
|
|
126bc61df6 | ||
|
|
c63e7ad392 |
2
.github/workflows/cont_integration.yml
vendored
2
.github/workflows/cont_integration.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.73.0
|
||||
- version: 1.77.1
|
||||
clippy: true
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
|
||||
13
.github/workflows/live-tests.yaml
vendored
13
.github/workflows/live-tests.yaml
vendored
@@ -27,8 +27,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
@@ -48,7 +48,8 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Build Swift package"
|
||||
run: bash ./bdk-swift/build-local-swift.sh
|
||||
working-directory: bdk-swift
|
||||
run: bash ./build-local-swift.sh
|
||||
|
||||
- name: "Run live Swift tests"
|
||||
working-directory: bdk-swift
|
||||
@@ -74,9 +75,11 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
|
||||
- name: "Install Rust 1.77.1"
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: 1.77.1
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
4
.github/workflows/publish-android.yaml
vendored
4
.github/workflows/publish-android.yaml
vendored
@@ -25,8 +25,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
12
.github/workflows/publish-jvm.yaml
vendored
12
.github/workflows/publish-jvm.yaml
vendored
@@ -24,8 +24,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install aarch64 Rust target"
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
@@ -54,8 +54,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install x86_64-pc-windows-msvc Rust target"
|
||||
run: rustup target add x86_64-pc-windows-msvc
|
||||
@@ -94,8 +94,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
|
||||
9
.github/workflows/publish-python.yaml
vendored
9
.github/workflows/publish-python.yaml
vendored
@@ -31,11 +31,12 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
# TODO 2: Other CI workflows use explicit Rust compiler versions, I think we should do the same here
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: "Install Rust 1.77.1"
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.77.1
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
|
||||
4
.github/workflows/test-android.yaml
vendored
4
.github/workflows/test-android.yaml
vendored
@@ -37,8 +37,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
4
.github/workflows/test-jvm.yaml
vendored
4
.github/workflows/test-jvm.yaml
vendored
@@ -32,8 +32,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Run JVM tests"
|
||||
run: |
|
||||
|
||||
8
.github/workflows/test-python.yaml
vendored
8
.github/workflows/test-python.yaml
vendored
@@ -40,10 +40,12 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: "Install Rust 1.77.1"
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.77.1
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ Changelog information can also be found in each release's git tag (which can be
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0-alpha.11]
|
||||
This release adds the new `Amount` type, as well as more fine-grain errors.
|
||||
|
||||
## [1.0.0-alpha.7]
|
||||
This release brings back into the 1.0 API a number of APIs from the 0.31 release, and adds the new flat file persistence feature, as well as more fine-grain errors.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ just publishlocal
|
||||
```
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
This library should compile with any combination of features with Rust 1.73.0.
|
||||
This library should compile with any combination of features with Rust 1.77.1.
|
||||
|
||||
## Contributing
|
||||
To add new structs and functions, see the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/) and the [uniffi-examples](https://thunderbiscuit.github.io/uniffi-examples/) repository.
|
||||
|
||||
@@ -38,22 +38,27 @@ _Note that Kotlin version `1.9.23` or later is required to build the library._
|
||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||
```
|
||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||
3. Install Rust (note that we are currently building using Rust 1.73.0):
|
||||
3. Install Rust (note that we are currently building using Rust 1.77.1):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
```
|
||||
4. Install required targets
|
||||
```sh
|
||||
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
```
|
||||
5. Install Android SDK and Build-Tools for API level 30+
|
||||
6. Setup `$ANDROID_SDK_ROOT` and `$ANDROID_NDK_ROOT` path variables (which are required by the
|
||||
build tool), for example (note that currently, NDK version 25.2.9519653 or above is required):
|
||||
6. Setup `ANDROID_SDK_ROOT` and `ANDROID_NDK_ROOT` path variables which are required by the build tool. Note that currently, NDK version 25.2.9519653 or above is required. For example:
|
||||
```shell
|
||||
export ANDROID_SDK_ROOT=~/Android/Sdk
|
||||
# macOS
|
||||
export ANDROID_SDK_ROOT=~/Library/Android/sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
||||
|
||||
# linux
|
||||
export ANDROID_SDK_ROOT=/usr/local/lib/android/sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
||||
```
|
||||
|
||||
7. Build kotlin bindings
|
||||
```sh
|
||||
# build Android library
|
||||
|
||||
@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.8-SNAPSHOT
|
||||
libraryVersion=1.0.0-alpha.11
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
test:
|
||||
./gradlew connectedAndroidTest
|
||||
|
||||
onetest TEST:
|
||||
./gradlew test --tests {{TEST}}
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
./gradlew buildAndroidLib
|
||||
|
||||
publishlocal:
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
rm -rf ./build/
|
||||
rm -rf ./lib/build/
|
||||
rm -rf ./plugins/build/
|
||||
rm -rf ./plugins/build/
|
||||
|
||||
publish-local:
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
|
||||
test:
|
||||
./gradlew connectedAndroidTest
|
||||
|
||||
test-specific TEST:
|
||||
./gradlew test --tests {{TEST}}
|
||||
@@ -8,10 +8,13 @@ import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveTxBuilderTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence.db"
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence3.db"
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
@@ -23,18 +26,22 @@ class LiveTxBuilderTest {
|
||||
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
@@ -44,21 +51,25 @@ class LiveTxBuilderTest {
|
||||
|
||||
@Test
|
||||
fun complexTxBuilder() {
|
||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
|
||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.TESTNET)
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
|
||||
val allRecipients: List<ScriptAmount> = listOf(
|
||||
ScriptAmount(recipient1.scriptPubkey(), 4200uL),
|
||||
ScriptAmount(recipient2.scriptPubkey(), 4200uL),
|
||||
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
|
||||
@@ -8,10 +8,13 @@ import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveWalletTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence.db"
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence2.db"
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
@@ -23,16 +26,20 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient: EsploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
val balance: Balance = wallet.getBalance()
|
||||
println("Balance: $balance")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
@@ -46,23 +53,23 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
println("New address: ${wallet.getAddress(AddressIndex.New).address}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.finish(wallet)
|
||||
|
||||
@@ -76,7 +83,7 @@ class LiveWalletTest {
|
||||
println("Txid is: ${tx.txid()}")
|
||||
|
||||
val txFee: ULong = wallet.calculateFee(tx)
|
||||
println("Tx fee is: ${txFee}")
|
||||
println("Tx fee is: $txFee")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||
|
||||
@@ -13,7 +13,7 @@ import kotlin.test.AfterTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineWalletTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence.db"
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence1.db"
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
@@ -44,7 +44,7 @@ class OfflineWalletTest {
|
||||
persistenceFilePath,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
|
||||
@@ -72,7 +72,7 @@ class OfflineWalletTest {
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.getBalance().total
|
||||
actual = wallet.getBalance().total.toSat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.bitcoindevkit.plugins
|
||||
|
||||
|
||||
val operatingSystem: OS = when {
|
||||
System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
|
||||
System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
|
||||
|
||||
@@ -17,6 +17,11 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
OS.OTHER -> throw Error("Cannot build Android library from current architecture")
|
||||
}
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set, stop build
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
throw IllegalStateException("ANDROID_NDK_ROOT environment variable is not set; cannot build library")
|
||||
}
|
||||
|
||||
// arm64-v8a is the most popular hardware architecture for Android
|
||||
val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
|
||||
|
||||
@@ -26,13 +31,6 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set then set it to github actions default
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
environment(
|
||||
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
|
||||
)
|
||||
}
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
@@ -56,13 +54,6 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set then set it to github actions default
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
environment(
|
||||
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
|
||||
)
|
||||
}
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
@@ -86,13 +77,6 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set then set it to github actions default
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
environment(
|
||||
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
|
||||
)
|
||||
}
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
@@ -139,14 +123,14 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
||||
dependsOn(moveNativeAndroidLibs)
|
||||
|
||||
// val libraryPath = "${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so"
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
val libraryPath = "${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so"
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
// The code above worked for uniffi 0.24.3 using the --library flag
|
||||
// The code below works for uniffi 0.23.0
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--config", "uniffi-android.toml", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--config", "uniffi-android.toml", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
283
bdk-ffi/Cargo.lock
generated
283
bdk-ffi/Cargo.lock
generated
@@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.11"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
|
||||
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -18,9 +18,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@@ -52,9 +52,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.79"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
@@ -111,9 +111,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
@@ -129,20 +129,22 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5"
|
||||
checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk"
|
||||
version = "1.0.0-alpha.9"
|
||||
version = "1.0.0-alpha.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ffe2761cc729d09bcaf88fe6e283ff5b5af0398c0eee855469295b59d6c6a9d"
|
||||
checksum = "65c23f2903ac5dbb7b35934ae319aadc946201e4fa51b652440bd1c8fa3080ee"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bdk_chain",
|
||||
"bdk_persist",
|
||||
"bip39",
|
||||
"bitcoin",
|
||||
"getrandom",
|
||||
@@ -155,23 +157,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bdk-ffi"
|
||||
version = "1.0.0-alpha.9"
|
||||
version = "1.0.0-alpha.11"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"bdk",
|
||||
"bdk_electrum",
|
||||
"bdk_esplora",
|
||||
"bdk_file_store",
|
||||
"bitcoin-internals",
|
||||
"esplora-client",
|
||||
"thiserror",
|
||||
"uniffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk_chain"
|
||||
version = "0.12.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd4e915470c6bb196a4c2515c6db3190dcdec482255d8f1022068646641cc8f"
|
||||
checksum = "440ec5b1c8911f126b540e05c98493b699b497a3cb90c5e9c5eee21cdd8d1e01"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"miniscript",
|
||||
@@ -179,10 +181,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk_esplora"
|
||||
version = "0.11.0"
|
||||
name = "bdk_electrum"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7b5358eb90cbf99f7725ec5b1786e25a869cacba555f74f70cc4bf453921c34"
|
||||
checksum = "44bbf3b0031651a37a48bdfab0c1d96a305b587f616593d34df9b1ff63efc4ff"
|
||||
dependencies = [
|
||||
"bdk_chain",
|
||||
"electrum-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk_esplora"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb5b46f8c256bc083640342bd0d35ec1963971f18800c3fee1a9189eda60ecd"
|
||||
dependencies = [
|
||||
"bdk_chain",
|
||||
"esplora-client",
|
||||
@@ -190,15 +202,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bdk_file_store"
|
||||
version = "0.9.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c19806b6b4e898e351e0bc2d8bd4cb96f7d8e2730c8b277e9e889c72dfb32de"
|
||||
checksum = "5dfd7e9a5edb8d384ea1836b0bcd4febdd3211815acc058d64c7e284776d69ab"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bdk_chain",
|
||||
"bdk_persist",
|
||||
"bincode",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk_persist"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aba103c2108dd0f0b452650043d21c449ae07ce866dbaea29a9c59899a5964f0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bdk_chain",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.10.0-beta"
|
||||
@@ -269,15 +293,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
@@ -290,9 +320,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.6"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d"
|
||||
checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -313,12 +343,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -328,9 +355,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.18"
|
||||
version = "4.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
|
||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -338,9 +365,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.18"
|
||||
version = "4.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
|
||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -350,11 +377,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
version = "4.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -362,9 +389,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
@@ -372,6 +399,23 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "electrum-client"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89008f106be6f303695522f2f4c1f28b40c3e8367ed8b3bb227f1f882cb52cc2"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"webpki-roots",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esplora-client"
|
||||
version = "0.7.0"
|
||||
@@ -396,9 +440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -428,6 +472,12 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex-conservative"
|
||||
version = "0.1.1"
|
||||
@@ -451,36 +501,36 @@ checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.67"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
@@ -574,18 +624,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -637,9 +687,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.10"
|
||||
version = "0.21.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
@@ -659,9 +709,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
@@ -716,27 +766,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.196"
|
||||
version = "1.0.198"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.196"
|
||||
version = "1.0.198"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -745,9 +795,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -780,15 +830,15 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
version = "2.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -797,9 +847,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
||||
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
@@ -915,7 +965,7 @@ dependencies = [
|
||||
"fs-err",
|
||||
"glob",
|
||||
"goblin",
|
||||
"heck",
|
||||
"heck 0.4.1",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"serde",
|
||||
@@ -1046,9 +1096,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.90"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1056,9 +1106,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.90"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@@ -1071,9 +1121,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.90"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1081,9 +1131,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.90"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1094,9 +1144,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.90"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
@@ -1113,6 +1163,28 @@ dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@@ -1124,13 +1196,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
@@ -1139,42 +1212,48 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "1.0.0-alpha.9"
|
||||
version = "1.0.0-alpha.11"
|
||||
homepage = "https://bitcoindevkit.org"
|
||||
repository = "https://github.com/bitcoindevkit/bdk"
|
||||
edition = "2018"
|
||||
@@ -18,11 +18,10 @@ path = "uniffi-bindgen.rs"
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk = { version = "1.0.0-alpha.9", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.11.0", default-features = false, features = ["std", "blocking"] }
|
||||
esplora-client = { version = "0.7.0", default-features = false, features = ["blocking-https-rustls"] }
|
||||
# bdk_esplora = { version = "0.10.0", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
|
||||
bdk_file_store = { version = "0.9.0" }
|
||||
bdk = { version = "1.0.0-alpha.11", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.13.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
||||
bdk_electrum = { version = "0.13.0" }
|
||||
bdk_file_store = { version = "0.11.0" }
|
||||
|
||||
uniffi = { version = "=0.26.1" }
|
||||
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
cargo build
|
||||
|
||||
check:
|
||||
cargo fmt
|
||||
cargo clippy
|
||||
|
||||
test:
|
||||
cargo test --lib
|
||||
cargo test --lib
|
||||
@@ -5,8 +5,40 @@ namespace bdk {};
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Error]
|
||||
enum Alpha3Error {
|
||||
"Generic"
|
||||
interface AddressError {
|
||||
Base58();
|
||||
Bech32();
|
||||
WitnessVersion(string error_message);
|
||||
WitnessProgram(string error_message);
|
||||
UncompressedPubkey();
|
||||
ExcessiveScriptSize();
|
||||
UnrecognizedScript();
|
||||
NetworkValidation(Network required, Network found, string address);
|
||||
OtherAddressErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface Bip32Error {
|
||||
CannotDeriveFromHardenedKey();
|
||||
Secp256k1(string error_message);
|
||||
InvalidChildNumber(u32 child_number);
|
||||
InvalidChildNumberFormat();
|
||||
InvalidDerivationPathFormat();
|
||||
UnknownVersion(string version);
|
||||
WrongExtendedKeyLength(u32 length);
|
||||
Base58(string error_message);
|
||||
Hex(string error_message);
|
||||
InvalidPublicKeyHexLength(u32 length);
|
||||
UnknownError(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface Bip39Error {
|
||||
BadWordCount(u64 word_count);
|
||||
UnknownWord(u64 index);
|
||||
BadEntropyBitCount(u64 bit_count);
|
||||
InvalidChecksum();
|
||||
AmbiguousLanguages(string languages);
|
||||
};
|
||||
|
||||
[Error]
|
||||
@@ -16,20 +48,78 @@ interface CalculateFeeError {
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface WalletCreationError {
|
||||
Io(string e);
|
||||
InvalidMagicBytes(sequence<u8> got, sequence<u8> expected);
|
||||
Descriptor();
|
||||
Write();
|
||||
Load();
|
||||
NotInitialized();
|
||||
LoadedGenesisDoesNotMatch();
|
||||
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
||||
interface CannotConnectError {
|
||||
Include(u32 height);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PersistenceError {
|
||||
Write(string e);
|
||||
interface CreateTxError {
|
||||
Descriptor(string error_message);
|
||||
Persist(string error_message);
|
||||
Policy(string error_message);
|
||||
SpendingPolicyRequired(string kind);
|
||||
Version0();
|
||||
Version1Csv();
|
||||
LockTime(string requested, string required);
|
||||
RbfSequence();
|
||||
RbfSequenceCsv(string rbf, string csv);
|
||||
FeeTooLow(u64 required);
|
||||
FeeRateTooLow(string required);
|
||||
NoUtxosSelected();
|
||||
OutputBelowDustLimit(u64 index);
|
||||
ChangePolicyDescriptor();
|
||||
CoinSelection(string error_message);
|
||||
InsufficientFunds(u64 needed, u64 available);
|
||||
NoRecipients();
|
||||
Psbt(string error_message);
|
||||
MissingKeyOrigin(string key);
|
||||
UnknownUtxo(string outpoint);
|
||||
MissingNonWitnessUtxo(string outpoint);
|
||||
MiniscriptPsbt(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface DescriptorError {
|
||||
InvalidHdKeyPath();
|
||||
InvalidDescriptorChecksum();
|
||||
HardenedDerivationXpub();
|
||||
MultiPath();
|
||||
Key(string error_message);
|
||||
Policy(string error_message);
|
||||
InvalidDescriptorCharacter(string char);
|
||||
Bip32(string error_message);
|
||||
Base58(string error_message);
|
||||
Pk(string error_message);
|
||||
Miniscript(string error_message);
|
||||
Hex(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface DescriptorKeyError {
|
||||
Parse(string error_message);
|
||||
InvalidKeyType();
|
||||
Bip32(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ElectrumError {
|
||||
IOError(string error_message);
|
||||
Json(string error_message);
|
||||
Hex(string error_message);
|
||||
Protocol(string error_message);
|
||||
Bitcoin(string error_message);
|
||||
AlreadySubscribed();
|
||||
NotSubscribed();
|
||||
InvalidResponse(string error_message);
|
||||
Message(string error_message);
|
||||
InvalidDNSNameError(string domain);
|
||||
MissingDomain();
|
||||
AllAttemptsErrored();
|
||||
SharedIOError(string error_message);
|
||||
CouldntLockReader();
|
||||
Mpsc();
|
||||
CouldNotCreateConnection(string error_message);
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
@@ -46,6 +136,15 @@ interface EsploraError {
|
||||
HeaderHashNotFound();
|
||||
InvalidHttpHeaderName(string name);
|
||||
InvalidHttpHeaderValue(string value);
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ExtractTxError {
|
||||
AbsurdFeeRate(u64 fee_rate);
|
||||
MissingInputValue();
|
||||
SendingTooMuch();
|
||||
OtherExtractTxErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
@@ -54,16 +153,45 @@ enum FeeRateError {
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface AddressError {
|
||||
Base58();
|
||||
Bech32();
|
||||
WitnessVersion(string error_message);
|
||||
WitnessProgram(string error_message);
|
||||
UncompressedPubkey();
|
||||
ExcessiveScriptSize();
|
||||
UnrecognizedScript();
|
||||
NetworkValidation(Network required, Network found, string address);
|
||||
OtherAddressError();
|
||||
interface ParseAmountError {
|
||||
Negative();
|
||||
TooBig();
|
||||
TooPrecise();
|
||||
InvalidFormat();
|
||||
InputTooLarge();
|
||||
InvalidCharacter(string error_message);
|
||||
UnknownDenomination(string error_message);
|
||||
PossiblyConfusingDenomination(string error_message);
|
||||
OtherParseAmountErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PersistenceError {
|
||||
Write(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PsbtParseError {
|
||||
PsbtEncoding(string error_message);
|
||||
Base64Encoding(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface SignerError {
|
||||
MissingKey();
|
||||
InvalidKey();
|
||||
UserCanceled();
|
||||
InputIndexOutOfRange();
|
||||
MissingNonWitnessUtxo();
|
||||
InvalidNonWitnessUtxo();
|
||||
MissingWitnessUtxo();
|
||||
MissingWitnessScript();
|
||||
MissingHdKeypath();
|
||||
NonStandardSighash();
|
||||
InvalidSighash();
|
||||
SighashError(string error_message);
|
||||
MiniscriptPsbt(string error_message);
|
||||
External(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
@@ -74,29 +202,7 @@ interface TransactionError {
|
||||
NonMinimalVarInt();
|
||||
ParseFailed();
|
||||
UnsupportedSegwitFlag(u8 flag);
|
||||
OtherTransactionError();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PsbtParseError {
|
||||
PsbtEncoding(string e);
|
||||
Base64Encoding(string e);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface DescriptorError {
|
||||
InvalidHdKeyPath();
|
||||
InvalidDescriptorChecksum();
|
||||
HardenedDerivationXpub();
|
||||
MultiPath();
|
||||
Key(string e);
|
||||
Policy(string e);
|
||||
InvalidDescriptorCharacter(string char);
|
||||
Bip32(string e);
|
||||
Base58(string e);
|
||||
Pk(string e);
|
||||
Miniscript(string e);
|
||||
Hex(string e);
|
||||
OtherTransactionErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
@@ -105,11 +211,15 @@ interface TxidParseError {
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ExtractTxError {
|
||||
AbsurdFeeRate(u64 fee_rate);
|
||||
MissingInputValue();
|
||||
SendingTooMuch();
|
||||
OtherExtractTransactionError();
|
||||
interface WalletCreationError {
|
||||
Io(string error_message);
|
||||
InvalidMagicBytes(sequence<u8> got, sequence<u8> expected);
|
||||
Descriptor();
|
||||
Persist(string error_message);
|
||||
NotInitialized();
|
||||
LoadedGenesisDoesNotMatch(string expected, string got);
|
||||
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
||||
LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -127,25 +237,18 @@ dictionary AddressInfo {
|
||||
KeychainKind keychain;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface AddressIndex {
|
||||
New();
|
||||
LastUnused();
|
||||
Peek(u32 index);
|
||||
};
|
||||
|
||||
dictionary Balance {
|
||||
u64 immature;
|
||||
Amount immature;
|
||||
|
||||
u64 trusted_pending;
|
||||
Amount trusted_pending;
|
||||
|
||||
u64 untrusted_pending;
|
||||
Amount untrusted_pending;
|
||||
|
||||
u64 confirmed;
|
||||
Amount confirmed;
|
||||
|
||||
u64 trusted_spendable;
|
||||
Amount trusted_spendable;
|
||||
|
||||
u64 total;
|
||||
Amount total;
|
||||
};
|
||||
|
||||
dictionary LocalOutput {
|
||||
@@ -171,24 +274,14 @@ dictionary CanonicalTx {
|
||||
ChainPosition chain_position;
|
||||
};
|
||||
|
||||
interface FullScanRequest {};
|
||||
|
||||
interface SyncRequest {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - wallet module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb, Throws=FeeRateError]
|
||||
constructor(u64 sat_per_vb);
|
||||
|
||||
[Name=from_sat_per_kwu]
|
||||
constructor(u64 sat_per_kwu);
|
||||
|
||||
u64 to_sat_per_vb_ceil();
|
||||
|
||||
u64 to_sat_per_vb_floor();
|
||||
|
||||
u64 to_sat_per_kwu();
|
||||
};
|
||||
|
||||
enum ChangeSpendPolicy {
|
||||
"ChangeAllowed",
|
||||
"OnlyChange",
|
||||
@@ -199,21 +292,25 @@ interface Wallet {
|
||||
[Throws=WalletCreationError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, string persistence_backend_path, Network network);
|
||||
|
||||
AddressInfo get_address(AddressIndex address_index);
|
||||
[Name=new_no_persist, Throws=DescriptorError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network);
|
||||
|
||||
[Throws=PersistenceError]
|
||||
AddressInfo try_get_internal_address(AddressIndex address_index);
|
||||
AddressInfo reveal_next_address(KeychainKind keychain);
|
||||
|
||||
Network network();
|
||||
|
||||
Balance get_balance();
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=CannotConnectError]
|
||||
void apply_update(Update update);
|
||||
|
||||
[Throws=PersistenceError]
|
||||
boolean commit();
|
||||
|
||||
boolean is_mine([ByRef] Script script);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=SignerError]
|
||||
boolean sign(Psbt psbt);
|
||||
|
||||
SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
|
||||
@@ -232,6 +329,10 @@ interface Wallet {
|
||||
sequence<LocalOutput> list_unspent();
|
||||
|
||||
sequence<LocalOutput> list_output();
|
||||
|
||||
FullScanRequest start_full_scan();
|
||||
|
||||
SyncRequest start_sync_with_revealed_spks();
|
||||
};
|
||||
|
||||
interface Update {};
|
||||
@@ -239,7 +340,7 @@ interface Update {};
|
||||
interface TxBuilder {
|
||||
constructor();
|
||||
|
||||
TxBuilder add_recipient([ByRef] Script script, u64 amount);
|
||||
TxBuilder add_recipient([ByRef] Script script, Amount amount);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||
|
||||
@@ -269,20 +370,18 @@ interface TxBuilder {
|
||||
|
||||
TxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=CreateTxError]
|
||||
Psbt finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface BumpFeeTxBuilder {
|
||||
constructor(string txid, FeeRate fee_rate);
|
||||
|
||||
BumpFeeTxBuilder allow_shrinking(Script script_pubkey);
|
||||
|
||||
BumpFeeTxBuilder enable_rbf();
|
||||
|
||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=CreateTxError]
|
||||
Psbt finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
@@ -293,30 +392,30 @@ interface BumpFeeTxBuilder {
|
||||
interface Mnemonic {
|
||||
constructor(WordCount word_count);
|
||||
|
||||
[Name=from_string, Throws=Alpha3Error]
|
||||
[Name=from_string, Throws=Bip39Error]
|
||||
constructor(string mnemonic);
|
||||
|
||||
[Name=from_entropy, Throws=Alpha3Error]
|
||||
[Name=from_entropy, Throws=Bip39Error]
|
||||
constructor(sequence<u8> entropy);
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface DerivationPath {
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=Bip32Error]
|
||||
constructor(string path);
|
||||
};
|
||||
|
||||
interface DescriptorSecretKey {
|
||||
constructor(Network network, [ByRef] Mnemonic mnemonic, string? password);
|
||||
|
||||
[Name=from_string, Throws=Alpha3Error]
|
||||
[Name=from_string, Throws=DescriptorKeyError]
|
||||
constructor(string secret_key);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorSecretKey derive([ByRef] DerivationPath path);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorSecretKey extend([ByRef] DerivationPath path);
|
||||
|
||||
DescriptorPublicKey as_public();
|
||||
@@ -327,13 +426,13 @@ interface DescriptorSecretKey {
|
||||
};
|
||||
|
||||
interface DescriptorPublicKey {
|
||||
[Name=from_string, Throws=Alpha3Error]
|
||||
[Name=from_string, Throws=DescriptorKeyError]
|
||||
constructor(string public_key);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorPublicKey derive([ByRef] DerivationPath path);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorPublicKey extend([ByRef] DerivationPath path);
|
||||
|
||||
string as_string();
|
||||
@@ -380,24 +479,45 @@ interface EsploraClient {
|
||||
constructor(string url);
|
||||
|
||||
[Throws=EsploraError]
|
||||
Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
|
||||
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 parallel_requests);
|
||||
|
||||
[Throws=EsploraError]
|
||||
Update sync(SyncRequest sync_request, u64 parallel_requests);
|
||||
|
||||
[Throws=EsploraError]
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_electrum crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface ElectrumClient {
|
||||
[Throws=ElectrumError]
|
||||
constructor(string url);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 batch_size, boolean fetch_prev_txouts);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
Update sync(SyncRequest sync_request, u64 batch_size, boolean fetch_prev_txouts);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
string broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk-ffi-defined types
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
u64 amount;
|
||||
Amount amount;
|
||||
};
|
||||
|
||||
dictionary SentAndReceivedValues {
|
||||
u64 sent;
|
||||
u64 received;
|
||||
Amount sent;
|
||||
Amount received;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -460,6 +580,14 @@ interface Transaction {
|
||||
i32 version();
|
||||
|
||||
sequence<u8> serialize();
|
||||
|
||||
u64 weight();
|
||||
|
||||
sequence<TxIn> input();
|
||||
|
||||
sequence<TxOut> output();
|
||||
|
||||
u32 lock_time();
|
||||
};
|
||||
|
||||
interface Psbt {
|
||||
@@ -476,3 +604,36 @@ dictionary OutPoint {
|
||||
string txid;
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
interface Amount {
|
||||
[Name=from_sat]
|
||||
constructor(u64 from_sat);
|
||||
|
||||
[Name=from_btc, Throws=ParseAmountError]
|
||||
constructor(f64 from_btc);
|
||||
|
||||
u64 to_sat();
|
||||
|
||||
f64 to_btc();
|
||||
};
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb, Throws=FeeRateError]
|
||||
constructor(u64 sat_per_vb);
|
||||
|
||||
[Name=from_sat_per_kwu]
|
||||
constructor(u64 sat_per_kwu);
|
||||
|
||||
u64 to_sat_per_vb_ceil();
|
||||
|
||||
u64 to_sat_per_vb_floor();
|
||||
|
||||
u64 to_sat_per_kwu();
|
||||
};
|
||||
|
||||
dictionary TxIn {
|
||||
OutPoint previous_output;
|
||||
Script script_sig;
|
||||
u32 sequence;
|
||||
sequence<sequence<u8>> witness;
|
||||
};
|
||||
|
||||
@@ -1,22 +1,60 @@
|
||||
use crate::error::{AddressError, PsbtParseError, TransactionError};
|
||||
use crate::error::{AddressError, FeeRateError, PsbtParseError, TransactionError};
|
||||
|
||||
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk::bitcoin::amount::ParseAmountError;
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
|
||||
use bdk::bitcoin::consensus::encode::serialize;
|
||||
use bdk::bitcoin::consensus::Decodable;
|
||||
use bdk::bitcoin::psbt::ExtractTxError;
|
||||
use bdk::bitcoin::Address as BdkAddress;
|
||||
use bdk::bitcoin::Amount as BdkAmount;
|
||||
use bdk::bitcoin::FeeRate as BdkFeeRate;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::bitcoin::TxIn as BdkTxIn;
|
||||
use bdk::bitcoin::Txid;
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Amount(pub(crate) BdkAmount);
|
||||
|
||||
impl Amount {
|
||||
pub fn from_sat(sat: u64) -> Self {
|
||||
Amount(BdkAmount::from_sat(sat))
|
||||
}
|
||||
|
||||
pub fn from_btc(btc: f64) -> Result<Self, ParseAmountError> {
|
||||
let bdk_amount = BdkAmount::from_btc(btc).map_err(ParseAmountError::from)?;
|
||||
Ok(Amount(bdk_amount))
|
||||
}
|
||||
|
||||
pub fn to_sat(&self) -> u64 {
|
||||
self.0.to_sat()
|
||||
}
|
||||
|
||||
pub fn to_btc(&self) -> f64 {
|
||||
self.0.to_btc()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Amount> for BdkAmount {
|
||||
fn from(amount: Amount) -> Self {
|
||||
amount.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAmount> for Amount {
|
||||
fn from(amount: BdkAmount) -> Self {
|
||||
Amount(amount)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Script(pub(crate) BdkScriptBuf);
|
||||
|
||||
@@ -38,60 +76,34 @@ impl From<BdkScriptBuf> for Script {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Address {
|
||||
inner: BdkAddress<NetworkChecked>,
|
||||
}
|
||||
pub struct Address(BdkAddress<NetworkChecked>);
|
||||
|
||||
impl Address {
|
||||
pub fn new(address: String, network: Network) -> Result<Self, AddressError> {
|
||||
let parsed_address = address.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()?;
|
||||
let network_checked_address = parsed_address.require_network(network)?;
|
||||
|
||||
Ok(Address {
|
||||
inner: network_checked_address,
|
||||
})
|
||||
Ok(Address(network_checked_address))
|
||||
}
|
||||
|
||||
/// alternative constructor
|
||||
// fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
|
||||
// BdkAddress::from_script(&script.inner, network)
|
||||
// .map(|a| Address { inner: a })
|
||||
// .map_err(|e| BdkError::Generic(e.to_string()))
|
||||
// }
|
||||
//
|
||||
// fn payload(&self) -> Payload {
|
||||
// match &self.inner.payload.clone() {
|
||||
// BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
|
||||
// pubkey_hash: pubkey_hash.to_vec(),
|
||||
// },
|
||||
// BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
|
||||
// script_hash: script_hash.to_vec(),
|
||||
// },
|
||||
// BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
|
||||
// version: *version,
|
||||
// program: program.clone(),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
*self.inner.network()
|
||||
*self.0.network()
|
||||
}
|
||||
|
||||
pub fn script_pubkey(&self) -> Arc<Script> {
|
||||
Arc::new(Script(self.inner.script_pubkey()))
|
||||
Arc::new(Script(self.0.script_pubkey()))
|
||||
}
|
||||
|
||||
pub fn to_qr_uri(&self) -> String {
|
||||
self.inner.to_qr_uri()
|
||||
self.0.to_qr_uri()
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
self.0.to_string()
|
||||
}
|
||||
|
||||
pub fn is_valid_for_network(&self, network: Network) -> bool {
|
||||
let address_str = self.inner.to_string();
|
||||
let address_str = self.0.to_string();
|
||||
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
|
||||
unchecked_address.is_valid_for_network(network)
|
||||
} else {
|
||||
@@ -102,166 +114,116 @@ impl Address {
|
||||
|
||||
impl From<Address> for BdkAddress {
|
||||
fn from(address: Address) -> Self {
|
||||
address.inner
|
||||
address.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAddress> for Address {
|
||||
fn from(address: BdkAddress) -> Self {
|
||||
Address { inner: address }
|
||||
Address(address)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Transaction {
|
||||
inner: BdkTransaction,
|
||||
}
|
||||
pub struct Transaction(BdkTransaction);
|
||||
|
||||
impl Transaction {
|
||||
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, TransactionError> {
|
||||
let mut decoder = Cursor::new(transaction_bytes);
|
||||
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
|
||||
Ok(Transaction { inner: tx })
|
||||
Ok(Transaction(tx))
|
||||
}
|
||||
|
||||
pub fn txid(&self) -> String {
|
||||
self.inner.txid().to_string()
|
||||
self.0.txid().to_string()
|
||||
}
|
||||
|
||||
// fn weight(&self) -> u64 {
|
||||
// self.inner.weight() as u64
|
||||
// }
|
||||
pub fn weight(&self) -> u64 {
|
||||
self.0.weight().to_wu()
|
||||
}
|
||||
|
||||
pub fn total_size(&self) -> u64 {
|
||||
self.inner.total_size() as u64
|
||||
self.0.total_size() as u64
|
||||
}
|
||||
|
||||
pub fn vsize(&self) -> u64 {
|
||||
self.inner.vsize() as u64
|
||||
self.0.vsize() as u64
|
||||
}
|
||||
|
||||
pub fn is_coinbase(&self) -> bool {
|
||||
self.inner.is_coinbase()
|
||||
self.0.is_coinbase()
|
||||
}
|
||||
|
||||
pub fn is_explicitly_rbf(&self) -> bool {
|
||||
self.inner.is_explicitly_rbf()
|
||||
self.0.is_explicitly_rbf()
|
||||
}
|
||||
|
||||
pub fn is_lock_time_enabled(&self) -> bool {
|
||||
self.inner.is_lock_time_enabled()
|
||||
self.0.is_lock_time_enabled()
|
||||
}
|
||||
|
||||
pub fn version(&self) -> i32 {
|
||||
self.inner.version.0
|
||||
self.0.version.0
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
serialize(&self.inner)
|
||||
serialize(&self.0)
|
||||
}
|
||||
|
||||
// fn lock_time(&self) -> u32 {
|
||||
// self.inner.lock_time.0
|
||||
// }
|
||||
pub fn input(&self) -> Vec<TxIn> {
|
||||
self.0.input.iter().map(|tx_in| tx_in.into()).collect()
|
||||
}
|
||||
|
||||
// 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 fn output(&self) -> Vec<TxOut> {
|
||||
self.0.output.iter().map(|tx_out| tx_out.into()).collect()
|
||||
}
|
||||
|
||||
pub fn lock_time(&self) -> u32 {
|
||||
self.0.lock_time.to_consensus_u32()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkTransaction> for Transaction {
|
||||
fn from(tx: BdkTransaction) -> Self {
|
||||
Transaction { inner: tx }
|
||||
Transaction(tx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BdkTransaction> for Transaction {
|
||||
fn from(tx: &BdkTransaction) -> Self {
|
||||
Transaction { inner: tx.clone() }
|
||||
Transaction(tx.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Transaction> for BdkTransaction {
|
||||
fn from(tx: &Transaction) -> Self {
|
||||
tx.inner.clone()
|
||||
tx.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Psbt {
|
||||
pub(crate) inner: Mutex<BdkPsbt>,
|
||||
}
|
||||
pub struct Psbt(pub(crate) Mutex<BdkPsbt>);
|
||||
|
||||
impl Psbt {
|
||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, PsbtParseError> {
|
||||
let psbt: BdkPsbt = BdkPsbt::from_str(&psbt_base64)?;
|
||||
Ok(Psbt {
|
||||
inner: Mutex::new(psbt),
|
||||
})
|
||||
Ok(Psbt(Mutex::new(psbt)))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> String {
|
||||
let psbt = self.inner.lock().unwrap().clone();
|
||||
let psbt = self.0.lock().unwrap().clone();
|
||||
psbt.to_string()
|
||||
}
|
||||
|
||||
// pub(crate) fn txid(&self) -> String {
|
||||
// let tx = self.inner.lock().unwrap().clone().extract_tx();
|
||||
// let txid = tx.txid();
|
||||
// txid.to_hex()
|
||||
// }
|
||||
|
||||
pub(crate) fn extract_tx(&self) -> Result<Arc<Transaction>, ExtractTxError> {
|
||||
let tx: BdkTransaction = self.inner.lock().unwrap().clone().extract_tx()?;
|
||||
let tx: BdkTransaction = self.0.lock().unwrap().clone().extract_tx()?;
|
||||
let transaction: Transaction = tx.into();
|
||||
Ok(Arc::new(transaction))
|
||||
}
|
||||
|
||||
// /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
|
||||
// ///
|
||||
// /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||
// pub(crate) fn combine(
|
||||
// &self,
|
||||
// other: Arc<PartiallySignedTransaction>,
|
||||
// ) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||
// let other_psbt = other.inner.lock().unwrap().clone();
|
||||
// let mut original_psbt = self.inner.lock().unwrap().clone();
|
||||
//
|
||||
// original_psbt.combine(other_psbt)?;
|
||||
// Ok(Arc::new(PartiallySignedTransaction {
|
||||
// inner: Mutex::new(original_psbt),
|
||||
// }))
|
||||
// }
|
||||
|
||||
// /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
|
||||
// /// If the PSBT is missing a TxOut for an input returns None.
|
||||
// pub(crate) fn fee_amount(&self) -> Option<u64> {
|
||||
// self.inner.lock().unwrap().fee_amount()
|
||||
// }
|
||||
|
||||
// /// The transaction's fee rate. This value will only be accurate if calculated AFTER the
|
||||
// /// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
|
||||
// /// transaction.
|
||||
// /// If the PSBT is missing a TxOut for an input returns None.
|
||||
// pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
|
||||
// self.inner.lock().unwrap().fee_rate().map(Arc::new)
|
||||
// }
|
||||
|
||||
// /// Serialize the PSBT data structure as a String of JSON.
|
||||
// pub(crate) fn json_serialize(&self) -> String {
|
||||
// let psbt = self.inner.lock().unwrap();
|
||||
// serde_json::to_string(psbt.deref()).unwrap()
|
||||
// }
|
||||
}
|
||||
|
||||
impl From<BdkPsbt> for Psbt {
|
||||
fn from(psbt: BdkPsbt) -> Self {
|
||||
Psbt {
|
||||
inner: Mutex::new(psbt),
|
||||
}
|
||||
Psbt(Mutex::new(psbt))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +251,28 @@ impl From<&BdkOutPoint> for OutPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[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(tx_in.script_sig.clone())),
|
||||
sequence: tx_in.sequence.0,
|
||||
witness: tx_in.witness.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxOut {
|
||||
pub value: u64,
|
||||
@@ -304,6 +288,35 @@ impl From<&BdkTxOut> for TxOut {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FeeRate(pub BdkFeeRate);
|
||||
|
||||
impl FeeRate {
|
||||
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
||||
let fee_rate: Option<BdkFeeRate> = BdkFeeRate::from_sat_per_vb(sat_per_vb);
|
||||
match fee_rate {
|
||||
Some(fee_rate) => Ok(FeeRate(fee_rate)),
|
||||
None => Err(FeeRateError::ArithmeticOverflow),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self {
|
||||
FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu))
|
||||
}
|
||||
|
||||
pub fn to_sat_per_vb_ceil(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_ceil()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_vb_floor(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_floor()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_kwu(&self) -> u64 {
|
||||
self.0.to_sat_per_kwu()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::bitcoin::Address;
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Descriptor {
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
@@ -65,7 +65,7 @@ impl Descriptor {
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
@@ -94,7 +94,7 @@ impl Descriptor {
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
@@ -122,7 +122,7 @@ impl Descriptor {
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
@@ -151,7 +151,7 @@ impl Descriptor {
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
@@ -179,7 +179,7 @@ impl Descriptor {
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
@@ -208,7 +208,7 @@ impl Descriptor {
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
@@ -236,7 +236,7 @@ impl Descriptor {
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
|
||||
95
bdk-ffi/src/electrum.rs
Normal file
95
bdk-ffi/src/electrum.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::error::ElectrumError;
|
||||
use crate::types::{FullScanRequest, SyncRequest};
|
||||
use crate::wallet::Update;
|
||||
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk::chain::spk_client::SyncResult as BdkSyncResult;
|
||||
use bdk::KeychainKind;
|
||||
use bdk_electrum::electrum_client::{Client as BdkBlockingClient, ElectrumApi};
|
||||
use bdk_electrum::{ElectrumExt, ElectrumFullScanResult, ElectrumSyncResult};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct ElectrumClient(BdkBlockingClient);
|
||||
|
||||
impl ElectrumClient {
|
||||
pub fn new(url: String) -> Result<Self, ElectrumError> {
|
||||
let client = BdkBlockingClient::new(url.as_str())?;
|
||||
Ok(Self(client))
|
||||
}
|
||||
|
||||
pub fn full_scan(
|
||||
&self,
|
||||
request: Arc<FullScanRequest>,
|
||||
stop_gap: u64,
|
||||
batch_size: u64,
|
||||
fetch_prev_txouts: bool,
|
||||
) -> Result<Arc<Update>, ElectrumError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkFullScanRequest<KeychainKind> = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||
|
||||
let electrum_result: ElectrumFullScanResult<KeychainKind> = self.0.full_scan(
|
||||
request,
|
||||
stop_gap as usize,
|
||||
batch_size as usize,
|
||||
fetch_prev_txouts,
|
||||
)?;
|
||||
let full_scan_result: BdkFullScanResult<KeychainKind> =
|
||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||
|
||||
let update = bdk::wallet::Update {
|
||||
last_active_indices: full_scan_result.last_active_indices,
|
||||
graph: full_scan_result.graph_update,
|
||||
chain: Some(full_scan_result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn sync(
|
||||
&self,
|
||||
request: Arc<SyncRequest>,
|
||||
batch_size: u64,
|
||||
fetch_prev_txouts: bool,
|
||||
) -> Result<Arc<Update>, ElectrumError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkSyncRequest = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||
|
||||
let electrum_result: ElectrumSyncResult =
|
||||
self.0
|
||||
.sync(request, batch_size as usize, fetch_prev_txouts)?;
|
||||
let sync_result: BdkSyncResult =
|
||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||
|
||||
let update = bdk::wallet::Update {
|
||||
last_active_indices: BTreeMap::default(),
|
||||
graph: sync_result.graph_update,
|
||||
chain: Some(sync_result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<String, ElectrumError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
self.0
|
||||
.transaction_broadcast(&bdk_transaction)
|
||||
.map_err(ElectrumError::from)
|
||||
.map(|txid| txid.to_string())
|
||||
}
|
||||
}
|
||||
2088
bdk-ffi/src/error.rs
2088
bdk-ffi/src/error.rs
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,15 @@
|
||||
use crate::error::EsploraError;
|
||||
use crate::wallet::{Update, Wallet};
|
||||
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::error::EsploraError;
|
||||
use crate::types::{FullScanRequest, SyncRequest};
|
||||
use crate::wallet::Update;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::wallet::Update as BdkUpdate;
|
||||
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk::chain::spk_client::SyncResult as BdkSyncResult;
|
||||
use bdk::KeychainKind;
|
||||
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||
use bdk_esplora::EsploraExt;
|
||||
|
||||
@@ -17,40 +23,56 @@ impl EsploraClient {
|
||||
Self(client)
|
||||
}
|
||||
|
||||
// This is a temporary solution for scanning. The long-term solution involves not passing
|
||||
// the wallet to the client at all.
|
||||
pub fn full_scan(
|
||||
&self,
|
||||
wallet: Arc<Wallet>,
|
||||
request: Arc<FullScanRequest>,
|
||||
stop_gap: u64,
|
||||
parallel_requests: u64,
|
||||
) -> Result<Arc<Update>, EsploraError> {
|
||||
let wallet = wallet.get_wallet();
|
||||
|
||||
let previous_tip = wallet.latest_checkpoint();
|
||||
let keychain_spks = wallet.all_unbounded_spk_iters().into_iter().collect();
|
||||
|
||||
let (update_graph, last_active_indices) = self
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkFullScanRequest<KeychainKind> = request
|
||||
.0
|
||||
.full_scan(keychain_spks, stop_gap as usize, parallel_requests as usize)
|
||||
.map_err(|e| EsploraError::from(*e))?;
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(EsploraError::RequestAlreadyConsumed)?;
|
||||
|
||||
let missing_heights = update_graph.missing_heights(wallet.local_chain());
|
||||
let chain_update = self
|
||||
.0
|
||||
.update_local_chain(previous_tip, missing_heights)
|
||||
.map_err(|e| EsploraError::from(*e))?;
|
||||
let result: BdkFullScanResult<KeychainKind> =
|
||||
self.0
|
||||
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices,
|
||||
graph: update_graph,
|
||||
chain: Some(chain_update),
|
||||
let update = bdk::wallet::Update {
|
||||
last_active_indices: result.last_active_indices,
|
||||
graph: result.graph_update,
|
||||
chain: Some(result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
// pub fn sync();
|
||||
pub fn sync(
|
||||
&self,
|
||||
request: Arc<SyncRequest>,
|
||||
parallel_requests: u64,
|
||||
) -> Result<Arc<Update>, EsploraError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkSyncRequest = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(EsploraError::RequestAlreadyConsumed)?;
|
||||
|
||||
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
|
||||
|
||||
let update = bdk::wallet::Update {
|
||||
last_active_indices: BTreeMap::default(),
|
||||
graph: result.graph_update,
|
||||
chain: Some(result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), EsploraError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
@@ -58,6 +80,4 @@ impl EsploraClient {
|
||||
.broadcast(&bdk_transaction)
|
||||
.map_err(EsploraError::from)
|
||||
}
|
||||
|
||||
// pub fn estimate_fee();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::error::Alpha3Error;
|
||||
use crate::error::{Bip32Error, Bip39Error, DescriptorKeyError};
|
||||
|
||||
use bdk::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
@@ -18,9 +18,7 @@ use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub(crate) struct Mnemonic {
|
||||
inner: BdkMnemonic,
|
||||
}
|
||||
pub(crate) struct Mnemonic(pub(crate) BdkMnemonic);
|
||||
|
||||
impl Mnemonic {
|
||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||
@@ -32,23 +30,23 @@ impl Mnemonic {
|
||||
let generated_key: GeneratedKey<_, BareCtx> =
|
||||
BdkMnemonic::generate_with_entropy((word_count, Language::English), entropy).unwrap();
|
||||
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||
Mnemonic { inner: mnemonic }
|
||||
Mnemonic(mnemonic)
|
||||
}
|
||||
|
||||
pub(crate) fn from_string(mnemonic: String) -> Result<Self, Alpha3Error> {
|
||||
pub(crate) fn from_string(mnemonic: String) -> Result<Self, Bip39Error> {
|
||||
BdkMnemonic::from_str(&mnemonic)
|
||||
.map(|m| Mnemonic { inner: m })
|
||||
.map_err(|_| Alpha3Error::Generic)
|
||||
.map(Mnemonic)
|
||||
.map_err(Bip39Error::from)
|
||||
}
|
||||
|
||||
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, Alpha3Error> {
|
||||
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, Bip39Error> {
|
||||
BdkMnemonic::from_entropy(entropy.as_slice())
|
||||
.map(|m| Mnemonic { inner: m })
|
||||
.map_err(|_| Alpha3Error::Generic)
|
||||
.map(Mnemonic)
|
||||
.map_err(Bip39Error::from)
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,23 +55,21 @@ pub(crate) struct DerivationPath {
|
||||
}
|
||||
|
||||
impl DerivationPath {
|
||||
pub(crate) fn new(path: String) -> Result<Self, Alpha3Error> {
|
||||
pub(crate) fn new(path: String) -> Result<Self, Bip32Error> {
|
||||
BdkDerivationPath::from_str(&path)
|
||||
.map(|x| DerivationPath {
|
||||
inner_mutex: Mutex::new(x),
|
||||
})
|
||||
.map_err(|_| Alpha3Error::Generic)
|
||||
.map_err(Bip32Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorSecretKey {
|
||||
pub(crate) inner: BdkDescriptorSecretKey,
|
||||
}
|
||||
pub struct DescriptorSecretKey(pub(crate) BdkDescriptorSecretKey);
|
||||
|
||||
impl DescriptorSecretKey {
|
||||
pub(crate) fn new(network: Network, mnemonic: &Mnemonic, password: Option<String>) -> Self {
|
||||
let mnemonic = mnemonic.inner.clone();
|
||||
let mnemonic = mnemonic.0.clone();
|
||||
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: None,
|
||||
@@ -81,27 +77,26 @@ impl DescriptorSecretKey {
|
||||
derivation_path: BdkDerivationPath::master(),
|
||||
wildcard: Wildcard::Unhardened,
|
||||
});
|
||||
Self {
|
||||
inner: descriptor_secret_key,
|
||||
}
|
||||
Self(descriptor_secret_key)
|
||||
}
|
||||
|
||||
pub(crate) fn from_string(private_key: String) -> Result<Self, Alpha3Error> {
|
||||
pub(crate) fn from_string(private_key: String) -> Result<Self, DescriptorKeyError> {
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
|
||||
.map_err(|_| Alpha3Error::Generic)?;
|
||||
Ok(Self {
|
||||
inner: descriptor_secret_key,
|
||||
})
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
Ok(Self(descriptor_secret_key))
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, Alpha3Error> {
|
||||
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_secret_key = &self.inner;
|
||||
let descriptor_secret_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorSecretKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
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)
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
|
||||
None => (descriptor_x_key.xkey.fingerprint(&secp), path),
|
||||
@@ -112,19 +107,17 @@ impl DescriptorSecretKey {
|
||||
derivation_path: BdkDerivationPath::default(),
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: derived_descriptor_secret_key,
|
||||
}))
|
||||
Ok(Arc::new(Self(derived_descriptor_secret_key)))
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, Alpha3Error> {
|
||||
let descriptor_secret_key = &self.inner;
|
||||
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let descriptor_secret_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorSecretKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
@@ -133,24 +126,20 @@ impl DescriptorSecretKey {
|
||||
derivation_path: extended_path,
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: extended_descriptor_secret_key,
|
||||
}))
|
||||
Ok(Arc::new(Self(extended_descriptor_secret_key)))
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_public(&self) -> Arc<DescriptorPublicKey> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = self.inner.to_public(&secp).unwrap();
|
||||
Arc::new(DescriptorPublicKey {
|
||||
inner: descriptor_public_key,
|
||||
})
|
||||
let descriptor_public_key = self.0.to_public(&secp).unwrap();
|
||||
Arc::new(DescriptorPublicKey(descriptor_public_key))
|
||||
}
|
||||
|
||||
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
||||
let inner = &self.inner;
|
||||
let inner = &self.0;
|
||||
let secret_bytes: Vec<u8> = match inner {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
@@ -167,33 +156,32 @@ impl DescriptorSecretKey {
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorPublicKey {
|
||||
pub(crate) inner: BdkDescriptorPublicKey,
|
||||
}
|
||||
pub struct DescriptorPublicKey(pub(crate) BdkDescriptorPublicKey);
|
||||
|
||||
impl DescriptorPublicKey {
|
||||
pub(crate) fn from_string(public_key: String) -> Result<Self, Alpha3Error> {
|
||||
pub(crate) fn from_string(public_key: String) -> Result<Self, DescriptorKeyError> {
|
||||
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
|
||||
.map_err(|_| Alpha3Error::Generic)?;
|
||||
Ok(Self {
|
||||
inner: descriptor_public_key,
|
||||
})
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
Ok(Self(descriptor_public_key))
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, Alpha3Error> {
|
||||
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = &self.inner;
|
||||
let descriptor_public_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
|
||||
match descriptor_public_key {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorPublicKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
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)
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
|
||||
None => (descriptor_x_key.xkey.fingerprint(), path),
|
||||
@@ -204,19 +192,17 @@ impl DescriptorPublicKey {
|
||||
derivation_path: BdkDerivationPath::default(),
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: derived_descriptor_public_key,
|
||||
}))
|
||||
Ok(Arc::new(Self(derived_descriptor_public_key)))
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, Alpha3Error> {
|
||||
let descriptor_public_key = &self.inner;
|
||||
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let descriptor_public_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_public_key {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorPublicKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||
@@ -225,16 +211,14 @@ impl DescriptorPublicKey {
|
||||
derivation_path: extended_path,
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: extended_descriptor_public_key,
|
||||
}))
|
||||
Ok(Arc::new(Self(extended_descriptor_public_key)))
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(Alpha3Error::Generic),
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +226,7 @@ impl DescriptorPublicKey {
|
||||
mod test {
|
||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
// use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use crate::error::Alpha3Error;
|
||||
use crate::error::DescriptorKeyError;
|
||||
use bdk::bitcoin::Network;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -254,7 +238,7 @@ mod test {
|
||||
fn derive_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, Alpha3Error> {
|
||||
) -> Result<Arc<DescriptorSecretKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.derive(&path)
|
||||
}
|
||||
@@ -262,7 +246,7 @@ mod test {
|
||||
fn extend_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, Alpha3Error> {
|
||||
) -> Result<Arc<DescriptorSecretKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.extend(&path)
|
||||
}
|
||||
@@ -270,7 +254,7 @@ mod test {
|
||||
fn derive_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, Alpha3Error> {
|
||||
) -> Result<Arc<DescriptorPublicKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.derive(&path)
|
||||
}
|
||||
@@ -278,7 +262,7 @@ mod test {
|
||||
fn extend_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, Alpha3Error> {
|
||||
) -> Result<Arc<DescriptorPublicKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.extend(&path)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod bitcoin;
|
||||
mod descriptor;
|
||||
mod electrum;
|
||||
mod error;
|
||||
mod esplora;
|
||||
mod keys;
|
||||
@@ -7,21 +8,32 @@ mod types;
|
||||
mod wallet;
|
||||
|
||||
use crate::bitcoin::Address;
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::FeeRate;
|
||||
use crate::bitcoin::OutPoint;
|
||||
use crate::bitcoin::Psbt;
|
||||
use crate::bitcoin::Script;
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::bitcoin::TxIn;
|
||||
use crate::bitcoin::TxOut;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::electrum::ElectrumClient;
|
||||
use crate::error::AddressError;
|
||||
use crate::error::Alpha3Error;
|
||||
use crate::error::Bip32Error;
|
||||
use crate::error::Bip39Error;
|
||||
use crate::error::CalculateFeeError;
|
||||
use crate::error::CannotConnectError;
|
||||
use crate::error::CreateTxError;
|
||||
use crate::error::DescriptorError;
|
||||
use crate::error::DescriptorKeyError;
|
||||
use crate::error::ElectrumError;
|
||||
use crate::error::EsploraError;
|
||||
use crate::error::ExtractTxError;
|
||||
use crate::error::FeeRateError;
|
||||
use crate::error::ParseAmountError;
|
||||
use crate::error::PersistenceError;
|
||||
use crate::error::PsbtParseError;
|
||||
use crate::error::SignerError;
|
||||
use crate::error::TransactionError;
|
||||
use crate::error::TxidParseError;
|
||||
use crate::error::WalletCreationError;
|
||||
@@ -30,14 +42,14 @@ use crate::keys::DerivationPath;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use crate::keys::Mnemonic;
|
||||
use crate::types::AddressIndex;
|
||||
use crate::types::AddressInfo;
|
||||
use crate::types::Balance;
|
||||
use crate::types::CanonicalTx;
|
||||
use crate::types::ChainPosition;
|
||||
use crate::types::FeeRate;
|
||||
use crate::types::FullScanRequest;
|
||||
use crate::types::LocalOutput;
|
||||
use crate::types::ScriptAmount;
|
||||
use crate::types::SyncRequest;
|
||||
use crate::wallet::BumpFeeTxBuilder;
|
||||
use crate::wallet::SentAndReceivedValues;
|
||||
use crate::wallet::TxBuilder;
|
||||
@@ -50,5 +62,3 @@ use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::KeychainKind;
|
||||
|
||||
uniffi::include_scaffolding!("bdk");
|
||||
|
||||
// TODO: TxIn, Payload
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::error::FeeRateError;
|
||||
|
||||
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
|
||||
|
||||
use bdk::bitcoin::FeeRate as BdkFeeRate;
|
||||
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
|
||||
use bdk::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor};
|
||||
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
||||
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
||||
use bdk::wallet::Balance as BdkBalance;
|
||||
use bdk::KeychainKind;
|
||||
use bdk::LocalOutput as BdkLocalOutput;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::bitcoin::Amount;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ChainPosition {
|
||||
@@ -45,38 +45,9 @@ impl From<BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHei
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FeeRate(pub BdkFeeRate);
|
||||
|
||||
impl FeeRate {
|
||||
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
||||
let fee_rate: Option<BdkFeeRate> = BdkFeeRate::from_sat_per_vb(sat_per_vb);
|
||||
match fee_rate {
|
||||
Some(fee_rate) => Ok(FeeRate(fee_rate)),
|
||||
None => Err(FeeRateError::ArithmeticOverflow),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self {
|
||||
FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu))
|
||||
}
|
||||
|
||||
pub fn to_sat_per_vb_ceil(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_ceil()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_vb_floor(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_floor()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_kwu(&self) -> u64 {
|
||||
self.0.to_sat_per_kwu()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScriptAmount {
|
||||
pub script: Arc<Script>,
|
||||
pub amount: u64,
|
||||
pub amount: Arc<Amount>,
|
||||
}
|
||||
|
||||
pub struct AddressInfo {
|
||||
@@ -95,71 +66,24 @@ impl From<BdkAddressInfo> for AddressInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AddressIndex {
|
||||
New,
|
||||
LastUnused,
|
||||
Peek { index: u32 },
|
||||
}
|
||||
|
||||
impl From<AddressIndex> for BdkAddressIndex {
|
||||
fn from(address_index: AddressIndex) -> Self {
|
||||
match address_index {
|
||||
AddressIndex::New => BdkAddressIndex::New,
|
||||
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||
AddressIndex::Peek { index } => BdkAddressIndex::Peek(index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 9: Peek is not correctly implemented
|
||||
impl From<&AddressIndex> for BdkAddressIndex {
|
||||
fn from(address_index: &AddressIndex) -> Self {
|
||||
match address_index {
|
||||
AddressIndex::New => BdkAddressIndex::New,
|
||||
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||
AddressIndex::Peek { index } => BdkAddressIndex::Peek(*index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Balance {
|
||||
pub immature: u64,
|
||||
pub trusted_pending: u64,
|
||||
pub untrusted_pending: u64,
|
||||
pub confirmed: u64,
|
||||
pub trusted_spendable: u64,
|
||||
pub total: u64,
|
||||
pub immature: Arc<Amount>,
|
||||
pub trusted_pending: Arc<Amount>,
|
||||
pub untrusted_pending: Arc<Amount>,
|
||||
pub confirmed: Arc<Amount>,
|
||||
pub trusted_spendable: Arc<Amount>,
|
||||
pub total: Arc<Amount>,
|
||||
}
|
||||
|
||||
impl From<BdkBalance> for Balance {
|
||||
fn from(bdk_balance: BdkBalance) -> Self {
|
||||
Balance {
|
||||
immature: bdk_balance.immature,
|
||||
trusted_pending: bdk_balance.trusted_pending,
|
||||
untrusted_pending: bdk_balance.untrusted_pending,
|
||||
confirmed: bdk_balance.confirmed,
|
||||
trusted_spendable: bdk_balance.trusted_spendable(),
|
||||
total: bdk_balance.total(),
|
||||
immature: Arc::new(bdk_balance.immature.into()),
|
||||
trusted_pending: Arc::new(bdk_balance.trusted_pending.into()),
|
||||
untrusted_pending: Arc::new(bdk_balance.untrusted_pending.into()),
|
||||
confirmed: Arc::new(bdk_balance.confirmed.into()),
|
||||
trusted_spendable: Arc::new(bdk_balance.trusted_spendable().into()),
|
||||
total: Arc::new(bdk_balance.total().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,3 +111,7 @@ impl From<BdkLocalOutput> for LocalOutput {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
|
||||
|
||||
pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest>>);
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
use crate::bitcoin::{OutPoint, Psbt, Script, Transaction};
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::error::{
|
||||
Alpha3Error, CalculateFeeError, PersistenceError, TxidParseError, WalletCreationError,
|
||||
CalculateFeeError, CannotConnectError, CreateTxError, DescriptorError, PersistenceError,
|
||||
SignerError, TxidParseError, WalletCreationError,
|
||||
};
|
||||
use crate::types::{
|
||||
AddressIndex, AddressInfo, Balance, CanonicalTx, FeeRate, LocalOutput, ScriptAmount,
|
||||
AddressInfo, Balance, CanonicalTx, FullScanRequest, LocalOutput, ScriptAmount, SyncRequest,
|
||||
};
|
||||
|
||||
use bdk::bitcoin::amount::Amount as BdkAmount;
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::wallet::{ChangeSet, Update as BdkUpdate};
|
||||
use bdk::SignOptions;
|
||||
use bdk::Wallet as BdkWallet;
|
||||
use bdk::{KeychainKind, SignOptions};
|
||||
use bdk_file_store::Store;
|
||||
|
||||
use std::collections::HashSet;
|
||||
@@ -24,7 +27,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
|
||||
const MAGIC_BYTES: &[u8] = "bdkffi".as_bytes();
|
||||
|
||||
pub struct Wallet {
|
||||
inner_mutex: Mutex<BdkWallet<Store<ChangeSet>>>,
|
||||
inner_mutex: Mutex<BdkWallet>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
@@ -38,7 +41,7 @@ impl Wallet {
|
||||
let change_descriptor = change_descriptor.map(|d| d.as_string_private());
|
||||
let db = Store::<ChangeSet>::open_or_create_new(MAGIC_BYTES, persistence_backend_path)?;
|
||||
|
||||
let wallet: bdk::wallet::Wallet<Store<ChangeSet>> =
|
||||
let wallet: BdkWallet =
|
||||
BdkWallet::new_or_load(&descriptor, change_descriptor.as_ref(), db, network)?;
|
||||
|
||||
Ok(Wallet {
|
||||
@@ -46,34 +49,50 @@ impl Wallet {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet<Store<ChangeSet>>> {
|
||||
pub fn new_no_persist(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Option<Arc<Descriptor>>,
|
||||
network: Network,
|
||||
) -> Result<Self, DescriptorError> {
|
||||
let descriptor = descriptor.as_string_private();
|
||||
let change_descriptor = change_descriptor.map(|d| d.as_string_private());
|
||||
|
||||
let wallet: BdkWallet =
|
||||
BdkWallet::new_no_persist(&descriptor, change_descriptor.as_ref(), network)?;
|
||||
|
||||
Ok(Wallet {
|
||||
inner_mutex: Mutex::new(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet> {
|
||||
self.inner_mutex.lock().expect("wallet")
|
||||
}
|
||||
|
||||
pub fn get_address(&self, address_index: AddressIndex) -> AddressInfo {
|
||||
pub fn reveal_next_address(
|
||||
&self,
|
||||
keychain_kind: KeychainKind,
|
||||
) -> Result<AddressInfo, PersistenceError> {
|
||||
self.get_wallet()
|
||||
.try_get_address(address_index.into())
|
||||
.unwrap()
|
||||
.into()
|
||||
.reveal_next_address(keychain_kind)
|
||||
.map(|address_info| address_info.into())
|
||||
.map_err(|e| PersistenceError::Write {
|
||||
error_message: e.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn apply_update(&self, update: Arc<Update>) -> Result<(), Alpha3Error> {
|
||||
pub fn apply_update(&self, update: Arc<Update>) -> Result<(), CannotConnectError> {
|
||||
self.get_wallet()
|
||||
.apply_update(update.0.clone())
|
||||
.map_err(|_| Alpha3Error::Generic)
|
||||
.map_err(CannotConnectError::from)
|
||||
}
|
||||
|
||||
// TODO: This is the fallible version of get_internal_address; should I rename it to get_internal_address?
|
||||
// It's a slight change of the API, the other option is to rename the get_address to try_get_address
|
||||
pub fn try_get_internal_address(
|
||||
&self,
|
||||
address_index: AddressIndex,
|
||||
) -> Result<AddressInfo, PersistenceError> {
|
||||
let address_info = self
|
||||
.get_wallet()
|
||||
.try_get_internal_address(address_index.into())?
|
||||
.into();
|
||||
Ok(address_info)
|
||||
pub fn commit(&self) -> Result<bool, PersistenceError> {
|
||||
self.get_wallet()
|
||||
.commit()
|
||||
.map_err(|e| PersistenceError::Write {
|
||||
error_message: e.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
@@ -81,7 +100,7 @@ impl Wallet {
|
||||
}
|
||||
|
||||
pub fn get_balance(&self) -> Balance {
|
||||
let bdk_balance: bdk::wallet::Balance = self.get_wallet().get_balance();
|
||||
let bdk_balance = self.get_wallet().get_balance();
|
||||
Balance::from(bdk_balance)
|
||||
}
|
||||
|
||||
@@ -93,16 +112,19 @@ impl Wallet {
|
||||
&self,
|
||||
psbt: Arc<Psbt>,
|
||||
// sign_options: Option<SignOptions>,
|
||||
) -> Result<bool, Alpha3Error> {
|
||||
let mut psbt = psbt.inner.lock().unwrap();
|
||||
) -> Result<bool, SignerError> {
|
||||
let mut psbt = psbt.0.lock().unwrap();
|
||||
self.get_wallet()
|
||||
.sign(&mut psbt, SignOptions::default())
|
||||
.map_err(|_| Alpha3Error::Generic)
|
||||
.map_err(SignerError::from)
|
||||
}
|
||||
|
||||
pub fn sent_and_received(&self, tx: &Transaction) -> SentAndReceivedValues {
|
||||
let (sent, received): (u64, u64) = self.get_wallet().sent_and_received(&tx.into());
|
||||
SentAndReceivedValues { sent, received }
|
||||
let (sent, received) = self.get_wallet().sent_and_received(&tx.into());
|
||||
SentAndReceivedValues {
|
||||
sent: Arc::new(sent.into()),
|
||||
received: Arc::new(received.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transactions(&self) -> Vec<CanonicalTx> {
|
||||
@@ -138,199 +160,28 @@ impl Wallet {
|
||||
pub fn list_output(&self) -> Vec<LocalOutput> {
|
||||
self.get_wallet().list_output().map(|o| o.into()).collect()
|
||||
}
|
||||
|
||||
pub fn start_full_scan(&self) -> Arc<FullScanRequest> {
|
||||
let request = self.get_wallet().start_full_scan();
|
||||
Arc::new(FullScanRequest(Mutex::new(Some(request))))
|
||||
}
|
||||
|
||||
pub fn start_sync_with_revealed_spks(&self) -> Arc<SyncRequest> {
|
||||
let request = self.get_wallet().start_sync_with_revealed_spks();
|
||||
Arc::new(SyncRequest(Mutex::new(Some(request))))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SentAndReceivedValues {
|
||||
pub sent: u64,
|
||||
pub received: u64,
|
||||
pub sent: Arc<Amount>,
|
||||
pub received: Arc<Amount>,
|
||||
}
|
||||
|
||||
pub struct Update(pub(crate) BdkUpdate);
|
||||
|
||||
// /// A Bitcoin wallet.
|
||||
// /// The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
|
||||
// /// 1. Output descriptors from which it can derive addresses.
|
||||
// /// 2. A Database where it tracks transactions and utxos related to the descriptors.
|
||||
// /// 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
|
||||
// impl Wallet {
|
||||
// pub fn new(
|
||||
// descriptor: Arc<Descriptor>,
|
||||
// change_descriptor: Option<Arc<Descriptor>>,
|
||||
// network: Network,
|
||||
// ) -> Result<Self, BdkError> {
|
||||
// let wallet = BdkWallet::new_no_persist()?;
|
||||
// Ok(Wallet {
|
||||
// inner: wallet,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Return whether or not a script is part of this wallet (either internal or external).
|
||||
// pub(crate) fn is_mine(&self, script: Arc<Script>) -> bool {
|
||||
// self.inner.is_mine(&script.inner)
|
||||
// }
|
||||
//
|
||||
// /// Sync the internal database with the blockchain.
|
||||
// // pub(crate) fn sync(
|
||||
// // &self,
|
||||
// // blockchain: &Blockchain,
|
||||
// // progress: Option<Box<dyn Progress>>,
|
||||
// // ) -> Result<(), BdkError> {
|
||||
// // let bdk_sync_opts = BdkSyncOptions {
|
||||
// // progress: progress.map(|p| {
|
||||
// // Box::new(ProgressHolder { progress: p })
|
||||
// // as Box<(dyn bdk::blockchain::Progress + 'static)>
|
||||
// // }),
|
||||
// // };
|
||||
// //
|
||||
// // let blockchain = blockchain.get_blockchain();
|
||||
// // self.get_wallet().sync(blockchain.deref(), bdk_sync_opts)
|
||||
// // }
|
||||
//
|
||||
// /// Return a derived address using the external descriptor, see AddressIndex for available address index selection
|
||||
// /// strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character)
|
||||
// /// then the same address will always be returned for any AddressIndex.
|
||||
// /// MIGRATION 1.0: The wallet needs to be mutated for this method to work... does that mean I should bring back the Mutex?
|
||||
// /// Is this thread-safe?
|
||||
// pub(crate) fn get_address(&mut self, address_index: AddressIndex) -> AddressInfo {
|
||||
// AddressInfo::from(self.inner.get_address(address_index.into()))
|
||||
// }
|
||||
//
|
||||
// /// Return a derived address using the internal (change) descriptor.
|
||||
// ///
|
||||
// /// If the wallet doesn't have an internal descriptor it will use the external descriptor.
|
||||
// ///
|
||||
// /// see [`AddressIndex`] for available address index selection strategies. If none of the keys
|
||||
// /// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
|
||||
// /// be returned for any [`AddressIndex`].
|
||||
// pub(crate) fn get_internal_address(&mut self, address_index: AddressIndex, ) -> AddressInfo {
|
||||
// AddressInfo::from(self.inner.get_internal_address(address_index.into()))
|
||||
// }
|
||||
//
|
||||
// /// Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates
|
||||
// /// on the internal database, which first needs to be Wallet.sync manually.
|
||||
// pub(crate) fn get_balance(&self) -> Balance {
|
||||
// Balance::from(self.inner.get_balance())
|
||||
// }
|
||||
//
|
||||
// /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
|
||||
// /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that
|
||||
// /// has the value true if the PSBT was finalized, or false otherwise.
|
||||
// ///
|
||||
// /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
|
||||
// /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
|
||||
// /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
|
||||
// /// in this library will.
|
||||
// pub(crate) fn sign(
|
||||
// &self,
|
||||
// psbt: &PartiallySignedTransaction,
|
||||
// sign_options: Option<SignOptions>,
|
||||
// ) -> Result<bool, BdkError> {
|
||||
// let mut psbt = psbt.inner.lock().unwrap();
|
||||
// self.inner.sign(
|
||||
// &mut psbt,
|
||||
// sign_options.map(SignOptions::into).unwrap_or_default(),
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// /// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
|
||||
// pub(crate) fn list_transactions(
|
||||
// &self,
|
||||
// include_raw: bool,
|
||||
// ) -> Result<Vec<TransactionDetails>, BdkError> {
|
||||
// let transaction_details = self.inner.list_transactions(include_raw)?;
|
||||
// Ok(transaction_details
|
||||
// .into_iter()
|
||||
// .map(TransactionDetails::from)
|
||||
// .collect())
|
||||
// }
|
||||
//
|
||||
// /// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
|
||||
// /// which first needs to be Wallet.sync manually.
|
||||
// pub(crate) fn list_unspent(&self) -> Result<Vec<LocalUtxo>, BdkError> {
|
||||
// let unspents: Vec<BdkLocalUtxo> = self.inner.list_unspent()?;
|
||||
// Ok(unspents.into_iter().map(LocalUtxo::from).collect())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Options for a software signer
|
||||
// ///
|
||||
// /// Adjust the behavior of our software signers and the way a transaction is finalized
|
||||
// #[derive(Debug, Clone, Default)]
|
||||
// pub struct SignOptions {
|
||||
// /// Whether the signer should trust the `witness_utxo`, if the `non_witness_utxo` hasn't been
|
||||
// /// provided
|
||||
// ///
|
||||
// /// Defaults to `false` to mitigate the "SegWit bug" which should trick the wallet into
|
||||
// /// paying a fee larger than expected.
|
||||
// ///
|
||||
// /// Some wallets, especially if relatively old, might not provide the `non_witness_utxo` for
|
||||
// /// SegWit transactions in the PSBT they generate: in those cases setting this to `true`
|
||||
// /// should correctly produce a signature, at the expense of an increased trust in the creator
|
||||
// /// of the PSBT.
|
||||
// ///
|
||||
// /// For more details see: <https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd>
|
||||
// pub trust_witness_utxo: bool,
|
||||
//
|
||||
// /// Whether the wallet should assume a specific height has been reached when trying to finalize
|
||||
// /// a transaction
|
||||
// ///
|
||||
// /// The wallet will only "use" a timelock to satisfy the spending policy of an input if the
|
||||
// /// timelock height has already been reached. This option allows overriding the "current height" to let the
|
||||
// /// wallet use timelocks in the future to spend a coin.
|
||||
// pub assume_height: Option<u32>,
|
||||
//
|
||||
// /// Whether the signer should use the `sighash_type` set in the PSBT when signing, no matter
|
||||
// /// what its value is
|
||||
// ///
|
||||
// /// Defaults to `false` which will only allow signing using `SIGHASH_ALL`.
|
||||
// pub allow_all_sighashes: bool,
|
||||
//
|
||||
// /// Whether to remove partial signatures from the PSBT inputs while finalizing PSBT.
|
||||
// ///
|
||||
// /// Defaults to `true` which will remove partial signatures during finalization.
|
||||
// pub remove_partial_sigs: bool,
|
||||
//
|
||||
// /// Whether to try finalizing the PSBT after the inputs are signed.
|
||||
// ///
|
||||
// /// Defaults to `true` which will try finalizing PSBT after inputs are signed.
|
||||
// pub try_finalize: bool,
|
||||
//
|
||||
// // Specifies which Taproot script-spend leaves we should sign for. This option is
|
||||
// // ignored if we're signing a non-taproot PSBT.
|
||||
// //
|
||||
// // Defaults to All, i.e., the wallet will sign all the leaves it has a key for.
|
||||
// // TODO pub tap_leaves_options: TapLeavesOptions,
|
||||
// /// Whether we should try to sign a taproot transaction with the taproot internal key
|
||||
// /// or not. This option is ignored if we're signing a non-taproot PSBT.
|
||||
// ///
|
||||
// /// Defaults to `true`, i.e., we always try to sign with the taproot internal key.
|
||||
// pub sign_with_tap_internal_key: bool,
|
||||
//
|
||||
// /// Whether we should grind ECDSA signature to ensure signing with low r
|
||||
// /// or not.
|
||||
// /// Defaults to `true`, i.e., we always grind ECDSA signature to sign with low r.
|
||||
// pub allow_grinding: bool,
|
||||
// }
|
||||
//
|
||||
// impl From<SignOptions> for BdkSignOptions {
|
||||
// fn from(sign_options: SignOptions) -> Self {
|
||||
// BdkSignOptions {
|
||||
// trust_witness_utxo: sign_options.trust_witness_utxo,
|
||||
// assume_height: sign_options.assume_height,
|
||||
// allow_all_sighashes: sign_options.allow_all_sighashes,
|
||||
// remove_partial_sigs: sign_options.remove_partial_sigs,
|
||||
// try_finalize: sign_options.try_finalize,
|
||||
// tap_leaves_options: Default::default(),
|
||||
// sign_with_tap_internal_key: sign_options.sign_with_tap_internal_key,
|
||||
// allow_grinding: sign_options.allow_grinding,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TxBuilder {
|
||||
pub(crate) recipients: Vec<(BdkScriptBuf, u64)>,
|
||||
pub(crate) recipients: Vec<(BdkScriptBuf, BdkAmount)>,
|
||||
pub(crate) utxos: Vec<OutPoint>,
|
||||
pub(crate) unspendable: HashSet<OutPoint>,
|
||||
pub(crate) change_policy: ChangeSpendPolicy,
|
||||
@@ -360,9 +211,9 @@ impl TxBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_recipient(&self, script: &Script, amount: u64) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScriptBuf, u64)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.0.clone(), amount)]);
|
||||
pub(crate) fn add_recipient(&self, script: &Script, amount: Arc<Amount>) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScriptBuf, BdkAmount)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.0.clone(), amount.0)]);
|
||||
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
@@ -373,7 +224,7 @@ impl TxBuilder {
|
||||
pub(crate) fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
|
||||
let recipients = recipients
|
||||
.iter()
|
||||
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount))
|
||||
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount.0)) //;
|
||||
.collect();
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
@@ -480,15 +331,7 @@ impl TxBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
/// Add data as an output using OP_RETURN.
|
||||
// pub(crate) fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// data,
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
|
||||
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, Alpha3Error> {
|
||||
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, CreateTxError> {
|
||||
// TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API?
|
||||
let mut wallet = wallet.get_wallet();
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
@@ -498,8 +341,9 @@ impl TxBuilder {
|
||||
tx_builder.change_policy(self.change_policy);
|
||||
if !self.utxos.is_empty() {
|
||||
let bdk_utxos: Vec<BdkOutPoint> = self.utxos.iter().map(BdkOutPoint::from).collect();
|
||||
let utxos: &[BdkOutPoint] = &bdk_utxos;
|
||||
tx_builder.add_utxos(utxos)?;
|
||||
tx_builder
|
||||
.add_utxos(&bdk_utxos)
|
||||
.map_err(CreateTxError::from)?;
|
||||
}
|
||||
if !self.unspendable.is_empty() {
|
||||
let bdk_unspendable: Vec<BdkOutPoint> =
|
||||
@@ -531,11 +375,8 @@ impl TxBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
// if !&self.data.is_empty() {
|
||||
// tx_builder.add_data(self.data.as_slice());
|
||||
// }
|
||||
|
||||
let psbt = tx_builder.finish()?;
|
||||
let psbt = tx_builder.finish().map_err(CreateTxError::from)?;
|
||||
|
||||
Ok(Arc::new(psbt.into()))
|
||||
}
|
||||
@@ -545,7 +386,6 @@ impl TxBuilder {
|
||||
pub(crate) struct BumpFeeTxBuilder {
|
||||
pub(crate) txid: String,
|
||||
pub(crate) fee_rate: Arc<FeeRate>,
|
||||
pub(crate) allow_shrinking: Option<Arc<Script>>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
}
|
||||
|
||||
@@ -554,18 +394,10 @@ impl BumpFeeTxBuilder {
|
||||
Self {
|
||||
txid,
|
||||
fee_rate,
|
||||
allow_shrinking: None,
|
||||
rbf: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn allow_shrinking(&self, script_pubkey: Arc<Script>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
allow_shrinking: Some(script_pubkey),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rbf: Some(RbfValue::Default),
|
||||
@@ -580,14 +412,13 @@ impl BumpFeeTxBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<Arc<Psbt>, Alpha3Error> {
|
||||
let txid = Txid::from_str(self.txid.as_str()).map_err(|_| Alpha3Error::Generic)?;
|
||||
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<Arc<Psbt>, CreateTxError> {
|
||||
let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo {
|
||||
outpoint: self.txid.clone(),
|
||||
})?;
|
||||
let mut wallet = wallet.get_wallet();
|
||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||
let mut tx_builder = wallet.build_fee_bump(txid).map_err(CreateTxError::from)?;
|
||||
tx_builder.fee_rate(self.fee_rate.0);
|
||||
if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||
tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
|
||||
}
|
||||
if let Some(rbf) = &self.rbf {
|
||||
match *rbf {
|
||||
RbfValue::Default => {
|
||||
@@ -598,7 +429,7 @@ impl BumpFeeTxBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
let psbt: BdkPsbt = tx_builder.finish().map_err(|_| Alpha3Error::Generic)?;
|
||||
let psbt: BdkPsbt = tx_builder.finish()?;
|
||||
|
||||
Ok(Arc::new(psbt.into()))
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ curl -s "https://get.sdkman.io" | bash
|
||||
source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
sdk install java 17.0.2-tem
|
||||
```
|
||||
2. Install Rust (note that we are currently building using Rust 1.73.0):
|
||||
2. Install Rust (note that we are currently building using Rust 1.77.1):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
```
|
||||
3. Clone this repository.
|
||||
```shell
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.8-SNAPSHOT
|
||||
libraryVersion=1.0.0-alpha.11
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
test:
|
||||
./gradlew test
|
||||
|
||||
offlinetests:
|
||||
./gradlew test -P excludeConnectedTests
|
||||
|
||||
onetest TEST:
|
||||
./gradlew test --tests {{TEST}}
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
./gradlew buildJvmLib
|
||||
|
||||
publishlocal:
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
rm -rf ./build/
|
||||
rm -rf ./lib/build/
|
||||
rm -rf ./plugins/build/
|
||||
|
||||
publish-local:
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
|
||||
test:
|
||||
./gradlew test
|
||||
|
||||
test-offline:
|
||||
./gradlew test -P excludeConnectedTests
|
||||
|
||||
test-specific TEST:
|
||||
./gradlew test --tests {{TEST}}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||
|
||||
class LiveElectrumClientTest {
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.SIGNET)
|
||||
val electrumClient: ElectrumClient = ElectrumClient(SIGNET_ELECTRUM_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = electrumClient.fullScan(fullScanRequest, 10uL, 10uL, false)
|
||||
wallet.applyUpdate(update)
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.txid()}")
|
||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||
println("Received ${sentAndReceived.received.toSat()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveMemoryWalletTest {
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.txid()}")
|
||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||
println("Received ${sentAndReceived.received.toSat()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveTransactionTests {
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
wallet.commit()
|
||||
println("Wallet balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val transaction: Transaction = wallet.transactions().first().transaction
|
||||
println("First transaction:")
|
||||
println("Txid: ${transaction.txid()}")
|
||||
println("Version: ${transaction.version()}")
|
||||
println("Total size: ${transaction.totalSize()}")
|
||||
println("Vsize: ${transaction.vsize()}")
|
||||
println("Weight: ${transaction.weight()}")
|
||||
println("Coinbase transaction: ${transaction.isCoinbase()}")
|
||||
println("Is explicitly RBF: ${transaction.isExplicitlyRbf()}")
|
||||
println("Inputs: ${transaction.input()}")
|
||||
println("Outputs: ${transaction.output()}")
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveTxBuilderTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
@@ -22,17 +25,21 @@ class LiveTxBuilderTest {
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
@@ -45,19 +52,23 @@ class LiveTxBuilderTest {
|
||||
fun complexTxBuilder() {
|
||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
|
||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.TESTNET)
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
|
||||
val allRecipients: List<ScriptAmount> = listOf(
|
||||
ScriptAmount(recipient1.scriptPubkey(), 4200uL),
|
||||
ScriptAmount(recipient2.scriptPubkey(), 4200uL),
|
||||
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
|
||||
@@ -5,6 +5,9 @@ import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveWalletTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
@@ -21,16 +24,18 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient: EsploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
// val esploraClient: EsploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
// val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
@@ -44,23 +49,23 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
|
||||
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
|
||||
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
println("New address: ${wallet.getAddress(AddressIndex.New).address.asString()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
||||
|
||||
assert(wallet.getBalance().total > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
|
||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class OfflineWalletTest {
|
||||
persistenceFilePath,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
|
||||
@@ -70,7 +70,7 @@ class OfflineWalletTest {
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.getBalance().total
|
||||
actual = wallet.getBalance().total.toSat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,20 +112,20 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
|
||||
// TODO 2: Is the Windows name the correct one?
|
||||
// TODO 3: This will not work on mac Intel (x86_64 architecture)
|
||||
// val libraryPath = when (operatingSystem) {
|
||||
// OS.LINUX -> "./target/x86_64-unknown-linux-gnu/release-smaller/libbdkffi.so"
|
||||
// OS.MAC -> "./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib"
|
||||
// OS.WINDOWS -> "./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll"
|
||||
// else -> throw Exception("Unsupported OS")
|
||||
// }
|
||||
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")
|
||||
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")
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin", "--no-format")
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
test:
|
||||
python -m unittest --verbose
|
||||
default:
|
||||
just --list
|
||||
|
||||
maclocalbuild:
|
||||
build-local-mac:
|
||||
bash ./scripts/generate-macos-arm64.sh && python3 setup.py bdist_wheel --verbose
|
||||
|
||||
clean:
|
||||
@@ -9,3 +9,6 @@ clean:
|
||||
rm -rf ./bdkpython.egg-info/
|
||||
rm -rf ./build/
|
||||
rm -rf ./dist/
|
||||
|
||||
test:
|
||||
python3 -m unittest --verbose
|
||||
@@ -4,14 +4,15 @@ set -euo pipefail
|
||||
${PYBIN}/python --version
|
||||
${PYBIN}/pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
rustup default 1.77.1
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
cargo build --profile release-smaller
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cargo run --bin uniffi-bindgen generate --library ./target/release-smaller/libbdkffi.so --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Copying linux libbdkffi.so..."
|
||||
cp ./target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so
|
||||
|
||||
|
||||
@@ -4,15 +4,16 @@ set -euo pipefail
|
||||
python3 --version
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
rustup default 1.77.1
|
||||
rustup target add aarch64-apple-darwin
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup target add aarch64-apple-darwin
|
||||
cargo build --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cargo run --bin uniffi-bindgen generate --library ./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Copying libraries libbdkffi.dylib..."
|
||||
cp ./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
|
||||
|
||||
@@ -4,15 +4,16 @@ set -euo pipefail
|
||||
python3 --version
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
rustup default 1.77.1
|
||||
rustup target add x86_64-apple-darwin
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup target add x86_64-apple-darwin
|
||||
cargo build --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cargo run --bin uniffi-bindgen generate --library ./target/x86_64-apple-darwin/release-smaller/libbdkffi.dylib --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Copying libraries libbdkffi.dylib..."
|
||||
cp ./target/x86_64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
|
||||
|
||||
@@ -4,15 +4,16 @@ set -euo pipefail
|
||||
python3 --version
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
rustup default 1.77.1
|
||||
rustup target add x86_64-pc-windows-msvc
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup target add x86_64-pc-windows-msvc
|
||||
cargo build --profile release-smaller --target x86_64-pc-windows-msvc
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cargo run --bin uniffi-bindgen generate --library ./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Copying libraries bdkffi.dll..."
|
||||
cp ./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll ../bdk-python/src/bdkpython/bdkffi.dll
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import bdkpython as bdk
|
||||
|
||||
setup(
|
||||
name="bdkpython",
|
||||
version="1.0.0a8.dev",
|
||||
version="1.0.0a11",
|
||||
description="The Python language bindings for the Bitcoin Development Kit",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
@@ -2,6 +2,9 @@ import bdkpython as bdk
|
||||
import unittest
|
||||
import os
|
||||
|
||||
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveTxBuilderTest(unittest.TestCase):
|
||||
|
||||
def tearDown(self) -> None:
|
||||
@@ -10,66 +13,74 @@ class LiveTxBuilderTest(unittest.TestCase):
|
||||
|
||||
def test_tx_builder(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
|
||||
update = esploraClient.full_scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
|
||||
update = esplora_client.full_scan(
|
||||
full_scan_request=full_scan_request,
|
||||
stop_gap=10,
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
|
||||
recipient = bdk.Address(
|
||||
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network = bdk.Network.TESTNET
|
||||
self.assertGreater(
|
||||
wallet.get_balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address.as_string()} and try again."
|
||||
)
|
||||
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
recipient = bdk.Address(
|
||||
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network=bdk.Network.SIGNET
|
||||
)
|
||||
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=bdk.Amount.from_sat(4200)).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
def complex_tx_builder(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
change_descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)",
|
||||
bdk.Network.TESTNET
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
|
||||
update = esploraClient.full_scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
|
||||
update = esplora_client.full_scan(
|
||||
full_scan_request=full_scan_request,
|
||||
stop_gap=10,
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
self.assertGreater(
|
||||
wallet.get_balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address.as_string()} and try again."
|
||||
)
|
||||
|
||||
recipient1 = bdk.Address(
|
||||
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network = bdk.Network.TESTNET
|
||||
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network=bdk.Network.SIGNET
|
||||
)
|
||||
recipient2 = bdk.Address(
|
||||
address = "tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6",
|
||||
network = bdk.Network.TESTNET
|
||||
address="tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6",
|
||||
network=bdk.Network.SIGNET
|
||||
)
|
||||
all_recipients = list(
|
||||
bdk.ScriptAmount(recipient1.script_pubkey, 4200),
|
||||
|
||||
@@ -2,6 +2,9 @@ import bdkpython as bdk
|
||||
import unittest
|
||||
import os
|
||||
|
||||
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveWalletTest(unittest.TestCase):
|
||||
|
||||
def tearDown(self) -> None:
|
||||
@@ -11,61 +14,72 @@ class LiveWalletTest(unittest.TestCase):
|
||||
def test_synced_balance(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
|
||||
update = esploraClient.full_scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
|
||||
update = esplora_client.full_scan(
|
||||
full_scan_request=full_scan_request,
|
||||
stop_gap=10,
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
self.assertGreater(
|
||||
wallet.get_balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address.as_string()} and try again."
|
||||
)
|
||||
|
||||
print(f"Transactions count: {len(wallet.transactions())}")
|
||||
transactions = wallet.transactions()[:3]
|
||||
for tx in transactions:
|
||||
sent_and_received = wallet.sent_and_received(tx.transaction)
|
||||
print(f"Transaction: {tx.transaction.txid()}")
|
||||
print(f"Sent {sent_and_received.sent}")
|
||||
print(f"Received {sent_and_received.received}")
|
||||
print(f"Sent {sent_and_received.sent.to_sat()}")
|
||||
print(f"Received {sent_and_received.received.to_sat()}")
|
||||
|
||||
|
||||
def test_broadcast_transaction(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
|
||||
update = esploraClient.full_scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
|
||||
update = esplora_client.full_scan(
|
||||
full_scan_request=full_scan_request,
|
||||
stop_gap=10,
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
|
||||
recipient = bdk.Address(
|
||||
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network = bdk.Network.TESTNET
|
||||
self.assertGreater(
|
||||
wallet.get_balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address.as_string()} and try again."
|
||||
)
|
||||
|
||||
psbt: bdk.Psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
# print(psbt.serialize())
|
||||
recipient = bdk.Address(
|
||||
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network=bdk.Network.SIGNET
|
||||
)
|
||||
|
||||
psbt: bdk.Psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=bdk.Amount.from_sat(4200)).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
walletDidSign = wallet.sign(psbt)
|
||||
@@ -77,7 +91,7 @@ class LiveWalletTest(unittest.TestCase):
|
||||
fee_rate = wallet.calculate_fee_rate(tx)
|
||||
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
esplora_client.broadcast(tx)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -19,7 +19,7 @@ class OfflineWalletTest(unittest.TestCase):
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
address_info: bdk.AddressInfo = wallet.get_address(bdk.AddressIndex.NEW())
|
||||
address_info: bdk.AddressInfo = wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL)
|
||||
|
||||
self.assertTrue(address_info.address.is_valid_for_network(bdk.Network.TESTNET), "Address is not valid for testnet network")
|
||||
self.assertTrue(address_info.address.is_valid_for_network(bdk.Network.SIGNET), "Address is not valid for signet network")
|
||||
@@ -40,7 +40,7 @@ class OfflineWalletTest(unittest.TestCase):
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
|
||||
self.assertEqual(wallet.get_balance().total, 0)
|
||||
self.assertEqual(wallet.get_balance().total.to_sat(), 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||
|
||||
final class LiveElectrumClientTests: XCTestCase {
|
||||
func testSyncedBalance() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .signet
|
||||
)
|
||||
let electrumClient: ElectrumClient = try ElectrumClient(url: SIGNET_ELECTRUM_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try electrumClient.fullScan(
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
batchSize: 10,
|
||||
fetchPrevTxouts: false
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.txid())")
|
||||
print("Sent \(sentAndReceived.sent.toSat())")
|
||||
print("Received \(sentAndReceived.received.toSat())")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
final class LiveMemoryWalletTests: XCTestCase {
|
||||
func testSyncedBalance() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.txid())")
|
||||
print("Sent \(sentAndReceived.sent.toSat())")
|
||||
print("Received \(sentAndReceived.received.toSat())")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
final class LiveTransactionTests: XCTestCase {
|
||||
func testSyncedBalance() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
guard let transaction = wallet.transactions().first?.transaction else {
|
||||
print("No transactions found")
|
||||
return
|
||||
}
|
||||
print("First transaction:")
|
||||
print("Txid: \(transaction.txid())")
|
||||
print("Version: \(transaction.version())")
|
||||
print("Total size: \(transaction.totalSize())")
|
||||
print("Vsize: \(transaction.vsize())")
|
||||
print("Weight: \(transaction.weight())")
|
||||
print("Coinbase transaction: \(transaction.isCoinbase())")
|
||||
print("Is explicitly RBF: \(transaction.isExplicitlyRbf())")
|
||||
print("Inputs: \(transaction.input())")
|
||||
print("Outputs: \(transaction.output())")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
final class LiveTxBuilderTests: XCTestCase {
|
||||
var dbFilePath: URL!
|
||||
|
||||
@@ -26,27 +29,34 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
func testTxBuilder() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
wallet: wallet,
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||
let psbt: Psbt = try TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200))
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2))
|
||||
.finish(wallet: wallet)
|
||||
|
||||
@@ -57,33 +67,40 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
func testComplexTxBuilder() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
network: Network.signet
|
||||
)
|
||||
let changeDescriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
network: Network.testnet
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
wallet: wallet,
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
let recipient1: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
|
||||
let recipient2: Address = try Address(address: "tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", network: .testnet)
|
||||
let recipient1: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||
let recipient2: Address = try Address(address: "tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", network: .signet)
|
||||
let allRecipients: [ScriptAmount] = [
|
||||
ScriptAmount(script: recipient1.scriptPubkey(), amount: 4200),
|
||||
ScriptAmount(script: recipient2.scriptPubkey(), amount: 4200)
|
||||
ScriptAmount(script: recipient1.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200)),
|
||||
ScriptAmount(script: recipient2.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200))
|
||||
]
|
||||
|
||||
let psbt: Psbt = try TxBuilder()
|
||||
@@ -93,7 +110,7 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
.enableRbf()
|
||||
.finish(wallet: wallet)
|
||||
|
||||
try! wallet.sign(psbt: psbt)
|
||||
let _ = try! wallet.sign(psbt: psbt)
|
||||
|
||||
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
final class LiveWalletTests: XCTestCase {
|
||||
var dbFilePath: URL!
|
||||
|
||||
@@ -26,61 +29,75 @@ final class LiveWalletTests: XCTestCase {
|
||||
func testSyncedBalance() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
wallet: wallet,
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0))
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.txid())")
|
||||
print("Sent \(sentAndReceived.sent)")
|
||||
print("Received \(sentAndReceived.received)")
|
||||
print("Sent \(sentAndReceived.sent.toSat())")
|
||||
print("Received \(sentAndReceived.received.toSat())")
|
||||
}
|
||||
}
|
||||
|
||||
func testBroadcastTransaction() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
wallet: wallet,
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
let _ = try wallet.commit()
|
||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.getBalance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
print("Balance: \(wallet.getBalance().total)")
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||
let psbt: Psbt = try
|
||||
TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200))
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2))
|
||||
.finish(wallet: wallet)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ final class OfflineWalletTests: XCTestCase {
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
)
|
||||
let addressInfo: AddressInfo = wallet.getAddress(addressIndex: AddressIndex.new)
|
||||
let addressInfo: AddressInfo = try wallet.revealNextAddress(keychain: KeychainKind.external)
|
||||
|
||||
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.testnet),
|
||||
"Address is not valid for testnet network")
|
||||
@@ -60,6 +60,6 @@ final class OfflineWalletTests: XCTestCase {
|
||||
network: .testnet
|
||||
)
|
||||
|
||||
XCTAssertEqual(wallet.getBalance().total, 0)
|
||||
XCTAssertEqual(wallet.getBalance().total.toSat(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# 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.
|
||||
|
||||
rustup install 1.73.0
|
||||
rustup default 1.77.1
|
||||
rustup component add rust-src
|
||||
rustup target add aarch64-apple-ios # iOS arm64
|
||||
rustup target add x86_64-apple-ios # iOS x86_64
|
||||
@@ -12,7 +12,6 @@ rustup target add aarch64-apple-darwin # mac M1
|
||||
rustup target add x86_64-apple-darwin # mac x86_64
|
||||
|
||||
cd ../bdk-ffi/ || exit
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
||||
|
||||
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
|
||||
@@ -20,6 +19,8 @@ cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-io
|
||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios
|
||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios-sim
|
||||
|
||||
cargo run --bin uniffi-bindgen generate --library ./target/aarch64-apple-ios/release-smaller/libbdkffi.dylib --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
||||
|
||||
mkdir -p target/lipo-ios-sim/release-smaller
|
||||
lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-apple-ios/release-smaller/libbdkffi.a -create -output target/lipo-ios-sim/release-smaller/libbdkffi.a
|
||||
mkdir -p target/lipo-macos/release-smaller
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
bash ./build-local-swift.sh
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
|
||||
test:
|
||||
swift test
|
||||
|
||||
offlinetests:
|
||||
swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
test-offline:
|
||||
swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
||||
Reference in New Issue
Block a user