Compare commits
36 Commits
release/1.
...
frost
Author | SHA1 | Date | |
---|---|---|---|
|
f2a29c5b05 | ||
|
34543311bb | ||
|
323eb08350 | ||
|
7e5897bd1c | ||
|
ffe0c8c1a4 | ||
|
cddd5f25d8 | ||
|
f4366ac49f | ||
|
e2abc3620a | ||
|
c66b48467a | ||
|
33026108a7 | ||
|
92d40a9f46 | ||
|
3b89af5a6e | ||
|
f66f8417cf | ||
|
92aeeab436 | ||
|
a2794f25b0 | ||
|
1a1920e7e3 | ||
|
6642c5808b | ||
|
e97e9b731c | ||
|
94d31ff7ed | ||
|
84f1329e84 | ||
|
efef60082b | ||
|
53afd9c238 | ||
|
b9128b0e6a | ||
|
9d3733389d | ||
|
65702f401b | ||
|
19b4e1159a | ||
|
e5e7aba208 | ||
|
9790d60324 | ||
|
093eb1fc7e | ||
|
5ef2bf8a1e | ||
|
7fc8da5451 | ||
|
5d41984377 | ||
|
89f803a753 | ||
|
1f7c782d49 | ||
|
af9346c4eb | ||
|
5cf25a50c4 |
2
.github/workflows/live-tests.yaml
vendored
2
.github/workflows/live-tests.yaml
vendored
@ -49,7 +49,7 @@ jobs:
|
|||||||
|
|
||||||
- name: "Build Swift package"
|
- name: "Build Swift package"
|
||||||
working-directory: bdk-swift
|
working-directory: bdk-swift
|
||||||
run: bash ./build-local-swift.sh
|
run: bash ./build-xcframework.sh
|
||||||
|
|
||||||
- name: "Run live Swift tests"
|
- name: "Run live Swift tests"
|
||||||
working-directory: bdk-swift
|
working-directory: bdk-swift
|
||||||
|
19
.github/workflows/publish-python.yaml
vendored
19
.github/workflows/publish-python.yaml
vendored
@ -1,22 +1,17 @@
|
|||||||
name: Publish bdkpython to PyPI
|
name: Publish bdkpython to PyPI
|
||||||
on: [workflow_dispatch]
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
|
||||||
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
|
|
||||||
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
|
|
||||||
# tries to load glibc and fails because it requires a more recent version.
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-manylinux2014-x86_64-wheels:
|
build-manylinux_2_28-x86_64-wheels:
|
||||||
name: "Build Manylinux 2014 x86_64 wheel"
|
name: "Build Manylinux 2.28 x86_64 wheel"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
container:
|
container:
|
||||||
image: quay.io/pypa/manylinux2014_x86_64
|
image: quay.io/pypa/manylinux_2_28_x86_64
|
||||||
env:
|
env:
|
||||||
PLAT: manylinux2014_x86_64
|
PLAT: manylinux_2_28_x86_64
|
||||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -43,11 +38,11 @@ jobs:
|
|||||||
- name: "Build wheel"
|
- name: "Build wheel"
|
||||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||||
# see issue #350 for more information
|
# see issue #350 for more information
|
||||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_28_x86_64 --verbose
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
|
||||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||||
|
|
||||||
build-macos-arm64-wheels:
|
build-macos-arm64-wheels:
|
||||||
@ -168,7 +163,7 @@ jobs:
|
|||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
needs: [build-manylinux2014-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
needs: [build-manylinux_2_28-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
17
.github/workflows/test-python.yaml
vendored
17
.github/workflows/test-python.yaml
vendored
@ -10,22 +10,17 @@ on:
|
|||||||
- "bdk-ffi/**"
|
- "bdk-ffi/**"
|
||||||
- "bdk-python/**"
|
- "bdk-python/**"
|
||||||
|
|
||||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
|
||||||
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
|
|
||||||
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
|
|
||||||
# tries to load glibc and fails because it requires a more recent version.
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-manylinux2014-x86_64-wheels:
|
build-manylinux_2_28-x86_64-wheels:
|
||||||
name: "Build and test Manylinux 2014 x86_64 wheels"
|
name: "Build and test Manylinux 2.28 x86_64 wheels"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
container:
|
container:
|
||||||
image: quay.io/pypa/manylinux2014_x86_64
|
image: quay.io/pypa/manylinux_2_28_x86_64
|
||||||
env:
|
env:
|
||||||
PLAT: manylinux2014_x86_64
|
PLAT: manylinux_2_28_x86_64
|
||||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -52,7 +47,7 @@ jobs:
|
|||||||
- name: "Build wheel"
|
- name: "Build wheel"
|
||||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||||
# see issue #350 for more information
|
# see issue #350 for more information
|
||||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_28_x86_64 --verbose
|
||||||
|
|
||||||
- name: "Install wheel"
|
- name: "Install wheel"
|
||||||
run: ${PYBIN}/pip install ./dist/*.whl
|
run: ${PYBIN}/pip install ./dist/*.whl
|
||||||
@ -63,7 +58,7 @@ jobs:
|
|||||||
- name: "Upload artifact test"
|
- name: "Upload artifact test"
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
|
||||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||||
|
|
||||||
build-macos-arm64-wheels:
|
build-macos-arm64-wheels:
|
||||||
|
4
.github/workflows/test-swift.yaml
vendored
4
.github/workflows/test-swift.yaml
vendored
@ -20,8 +20,8 @@ jobs:
|
|||||||
|
|
||||||
- name: "Build Swift package"
|
- name: "Build Swift package"
|
||||||
working-directory: bdk-swift
|
working-directory: bdk-swift
|
||||||
run: bash ./build-local-swift.sh
|
run: bash ./build-xcframework.sh
|
||||||
|
|
||||||
- name: "Run Swift tests"
|
- name: "Run Swift tests"
|
||||||
working-directory: bdk-swift
|
working-directory: bdk-swift
|
||||||
run: swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
run: swift test --skip LiveElectrumClientTests --skip LiveMemoryWalletTests --skip LiveTransactionTests --skip LiveTxBuilderTests --skip LiveWalletTests
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -31,6 +31,8 @@ bdkFFI.h
|
|||||||
BitcoinDevKit.swift
|
BitcoinDevKit.swift
|
||||||
bdk.swift
|
bdk.swift
|
||||||
.build
|
.build
|
||||||
|
*.xcframework/
|
||||||
|
Info.plist
|
||||||
|
|
||||||
# Python related
|
# Python related
|
||||||
__pycache__
|
__pycache__
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -3,13 +3,27 @@ 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).
|
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]
|
## [v1.0.0-alpha.11]
|
||||||
This release adds the new `Amount` type, as well as more fine-grain errors.
|
This release brings the latest alpha 11 release of the Rust bdk_wallet library, as well as the new Electrum client, the new memory wallet, and a whole lot of new types and APIs across the library. Also of note are the much simpler-to-use full_scan and sync workflows for syncing wallets.
|
||||||
|
|
||||||
## [1.0.0-alpha.7]
|
Added:
|
||||||
|
- `Amount` type [#533]
|
||||||
|
- `TxIn` type [#536]
|
||||||
|
- `Transaction.input()` method [#536]
|
||||||
|
- `Transaction.output()` method [#536]
|
||||||
|
- `Transaction.lock_time()` method [#536]
|
||||||
|
- `Electrum` client [#535]
|
||||||
|
- Memory wallet [#528]
|
||||||
|
|
||||||
|
[#528]: https://github.com/bitcoindevkit/bdk-ffi/pull/528
|
||||||
|
[#533]: https://github.com/bitcoindevkit/bdk-ffi/pull/533
|
||||||
|
[#535]: https://github.com/bitcoindevkit/bdk-ffi/pull/535
|
||||||
|
[#536]: https://github.com/bitcoindevkit/bdk-ffi/pull/536
|
||||||
|
|
||||||
|
## [v1.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.
|
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.
|
||||||
|
|
||||||
## [1.0.0-alpha.2a]
|
## [v1.0.0-alpha.2a]
|
||||||
This release is the first alpha release of the 1.0 API for the bindings libraries. Here is what is now available:
|
This release is the first alpha release of the 1.0 API for the bindings libraries. Here is what is now available:
|
||||||
- Create and recover wallets using descriptors, including the four descriptor templates
|
- Create and recover wallets using descriptors, including the four descriptor templates
|
||||||
- Sync a wallet using a blocking Esplora client
|
- Sync a wallet using a blocking Esplora client
|
||||||
@ -17,7 +31,7 @@ This release is the first alpha release of the 1.0 API for the bindings librarie
|
|||||||
- Create and sign transactions using the transaction builder
|
- Create and sign transactions using the transaction builder
|
||||||
- Broadcast transactions
|
- Broadcast transactions
|
||||||
|
|
||||||
## [0.31.0]
|
## [v0.31.0]
|
||||||
This release updates the bindings libraries to bdk version 0.29.0, updating rust-bitcoin to version 0.30.2.
|
This release updates the bindings libraries to bdk version 0.29.0, updating rust-bitcoin to version 0.30.2.
|
||||||
|
|
||||||
- APIs Changed:
|
- APIs Changed:
|
||||||
@ -29,7 +43,7 @@ This release updates the bindings libraries to bdk version 0.29.0, updating rust
|
|||||||
|
|
||||||
[#443]: https://github.com/bitcoindevkit/bdk-ffi/pull/443
|
[#443]: https://github.com/bitcoindevkit/bdk-ffi/pull/443
|
||||||
|
|
||||||
## [0.30.0]
|
## [v0.30.0]
|
||||||
This release has a new API and a few internal optimizations and refactorings.
|
This release has a new API and a few internal optimizations and refactorings.
|
||||||
|
|
||||||
- APIs Added
|
- APIs Added
|
||||||
@ -37,7 +51,7 @@ This release has a new API and a few internal optimizations and refactorings.
|
|||||||
|
|
||||||
[#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388
|
[#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388
|
||||||
|
|
||||||
## [0.29.0]
|
## [v0.29.0]
|
||||||
This release has a number of new APIs, and adds support for Windows in bdk-jvm.
|
This release has a number of new APIs, and adds support for Windows in bdk-jvm.
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
@ -248,8 +262,9 @@ Changelog
|
|||||||
|
|
||||||
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
||||||
|
|
||||||
[1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.2a...v1.0.0-alpha.7
|
[v1.0.0-alpha.11]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.7...v1.0.0-alpha.11
|
||||||
[1.0.0-alpha.2a]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.31.0...v1.0.0-alpha.2a
|
[v1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.2a...v1.0.0-alpha.7
|
||||||
|
[v1.0.0-alpha.2a]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.31.0...v1.0.0-alpha.2a
|
||||||
[v0.31.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.30.0...v0.31.0
|
[v0.31.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.30.0...v0.31.0
|
||||||
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
|
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
|
||||||
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0
|
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0
|
||||||
|
@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
libraryVersion=1.0.0-alpha.10-SNAPSHOT
|
libraryVersion=1.0.0-alpha.12-SNAPSHOT
|
||||||
|
@ -17,4 +17,4 @@ test:
|
|||||||
./gradlew connectedAndroidTest
|
./gradlew connectedAndroidTest
|
||||||
|
|
||||||
test-specific TEST:
|
test-specific TEST:
|
||||||
./gradlew test --tests {{TEST}}
|
./gradlew test --tests {{TEST}}
|
||||||
|
@ -18,7 +18,7 @@ android {
|
|||||||
compileSdk = 34
|
compileSdk = 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 21
|
minSdk = 24
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
|
@ -13,8 +13,9 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
|||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class LiveTxBuilderTest {
|
class LiveTxBuilderTest {
|
||||||
private val persistenceFilePath = InstrumentationRegistry
|
private val persistenceFilePath = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence3.sqlite"
|
||||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence3.db"
|
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||||
|
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||||
|
|
||||||
@AfterTest
|
@AfterTest
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
@ -26,17 +27,15 @@ class LiveTxBuilderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testTxBuilder() {
|
fun testTxBuilder() {
|
||||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
|
||||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"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.SIGNET)
|
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||||
@ -51,18 +50,16 @@ class LiveTxBuilderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun complexTxBuilder() {
|
fun complexTxBuilder() {
|
||||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
val wallet = Wallet(descriptor, changeDescriptor, 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 esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
|
||||||
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||||
|
@ -14,7 +14,9 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class LiveWalletTest {
|
class LiveWalletTest {
|
||||||
private val persistenceFilePath = InstrumentationRegistry
|
private val persistenceFilePath = InstrumentationRegistry
|
||||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence2.db"
|
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence2.sqlite"
|
||||||
|
private val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||||
|
|
||||||
@AfterTest
|
@AfterTest
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
@ -26,26 +28,24 @@ class LiveWalletTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSyncedBalance() {
|
fun testSyncedBalance() {
|
||||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
|
||||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
val balance: Balance = wallet.balance()
|
||||||
val balance: Balance = wallet.getBalance()
|
|
||||||
println("Balance: $balance")
|
println("Balance: $balance")
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Transactions count: ${wallet.transactions().count()}")
|
println("Transactions count: ${wallet.transactions().count()}")
|
||||||
val transactions = wallet.transactions().take(3)
|
val transactions = wallet.transactions().take(3)
|
||||||
for (tx in transactions) {
|
for (tx in transactions) {
|
||||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||||
println("Transaction: ${tx.transaction.txid()}")
|
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||||
println("Sent ${sentAndReceived.sent}")
|
println("Sent ${sentAndReceived.sent}")
|
||||||
println("Received ${sentAndReceived.received}")
|
println("Received ${sentAndReceived.received}")
|
||||||
}
|
}
|
||||||
@ -53,16 +53,14 @@ class LiveWalletTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBroadcastTransaction() {
|
fun testBroadcastTransaction() {
|
||||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
|
||||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,10 +78,10 @@ class LiveWalletTest {
|
|||||||
assertTrue(walletDidSign)
|
assertTrue(walletDidSign)
|
||||||
|
|
||||||
val tx: Transaction = psbt.extractTx()
|
val tx: Transaction = psbt.extractTx()
|
||||||
println("Txid is: ${tx.txid()}")
|
println("Txid is: ${tx.computeTxid()}")
|
||||||
|
|
||||||
val txFee: ULong = wallet.calculateFee(tx)
|
val txFee: Amount = wallet.calculateFee(tx)
|
||||||
println("Tx fee is: $txFee")
|
println("Tx fee is: ${txFee.toSat()}")
|
||||||
|
|
||||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||||
|
@ -15,7 +15,7 @@ class OfflineDescriptorTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||||
actual = descriptor.asString()
|
actual = descriptor.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ import kotlin.test.AfterTest
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class OfflineWalletTest {
|
class OfflineWalletTest {
|
||||||
private val persistenceFilePath = InstrumentationRegistry
|
private val persistenceFilePath = InstrumentationRegistry
|
||||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence1.db"
|
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence1.sqlite"
|
||||||
|
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||||
|
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
|
||||||
|
|
||||||
@AfterTest
|
@AfterTest
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
@ -29,19 +31,14 @@ class OfflineWalletTest {
|
|||||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||||
|
|
||||||
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
assertTrue(descriptor.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNewAddress() {
|
fun testNewAddress() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
Network.TESTNET
|
|
||||||
)
|
|
||||||
val wallet: Wallet = Wallet(
|
val wallet: Wallet = Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
null,
|
changeDescriptor,
|
||||||
persistenceFilePath,
|
|
||||||
Network.TESTNET
|
Network.TESTNET
|
||||||
)
|
)
|
||||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||||
@ -52,27 +49,22 @@ class OfflineWalletTest {
|
|||||||
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
|
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||||
actual = addressInfo.address.asString()
|
actual = addressInfo.address.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBalance() {
|
fun testBalance() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
Network.TESTNET
|
|
||||||
)
|
|
||||||
val wallet: Wallet = Wallet(
|
val wallet: Wallet = Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
null,
|
changeDescriptor,
|
||||||
persistenceFilePath,
|
|
||||||
Network.TESTNET
|
Network.TESTNET
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected = 0uL,
|
expected = 0uL,
|
||||||
actual = wallet.getBalance().total.toSat()
|
actual = wallet.balance().total.toSat()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
environment(
|
environment(
|
||||||
// add build toolchain to PATH
|
// add build toolchain to PATH
|
||||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||||
Pair("AR", "llvm-ar"),
|
Pair("AR", "llvm-ar"),
|
||||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
|
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android24-clang"),
|
||||||
Pair("CC", "aarch64-linux-android21-clang")
|
Pair("CC", "aarch64-linux-android24-clang")
|
||||||
)
|
)
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
@ -57,10 +57,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
environment(
|
environment(
|
||||||
// add build toolchain to PATH
|
// add build toolchain to PATH
|
||||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||||
Pair("AR", "llvm-ar"),
|
Pair("AR", "llvm-ar"),
|
||||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
|
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android24-clang"),
|
||||||
Pair("CC", "x86_64-linux-android21-clang")
|
Pair("CC", "x86_64-linux-android24-clang")
|
||||||
)
|
)
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
@ -80,10 +80,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
environment(
|
environment(
|
||||||
// add build toolchain to PATH
|
// add build toolchain to PATH
|
||||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||||
Pair("AR", "llvm-ar"),
|
Pair("AR", "llvm-ar"),
|
||||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi21-clang"),
|
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi24-clang"),
|
||||||
Pair("CC", "armv7a-linux-androideabi21-clang")
|
Pair("CC", "armv7a-linux-androideabi24-clang")
|
||||||
)
|
)
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
|
637
bdk-ffi/Cargo.lock
generated
637
bdk-ffi/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -18,20 +18,24 @@ path = "uniffi-bindgen.rs"
|
|||||||
default = ["uniffi/cli"]
|
default = ["uniffi/cli"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdk = { version = "1.0.0-alpha.11", features = ["all-keys", "keys-bip39"] }
|
bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys", "keys-bip39"] }
|
||||||
bdk_esplora = { version = "0.13.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
bdk_esplora = { version = "0.15.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
||||||
bdk_electrum = { version = "0.13.0" }
|
# NOTE: This is a temporary workaround to use the electrum-client with the use-rustls-ring feature. It points to a fork
|
||||||
bdk_file_store = { version = "0.11.0" }
|
# of bdk in which the bdk_electrum library uses the electrum-client with the use-rustls-ring feature.
|
||||||
|
bdk_electrum = { git = "https://github.com/thunderbiscuit/bdk/", package = "bdk_electrum", branch = "feature/electrum-client-ring-ffi-alpha13", default-features = false, features = ["use-rustls-ring"] }
|
||||||
uniffi = { version = "=0.26.1" }
|
# bdk_electrum = { version = "0.15.0" }
|
||||||
|
bdk_sqlite = { version = "0.2.0" }
|
||||||
|
bdk_bitcoind_rpc = { version = "0.12.0" }
|
||||||
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
|
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
|
||||||
|
|
||||||
|
uniffi = { version = "=0.28.0" }
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.58"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
uniffi = { version = "=0.26.1", features = ["build"] }
|
uniffi = { version = "=0.28.0", features = ["build"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
uniffi = { version = "=0.26.1", features = ["bindgen-tests"] }
|
uniffi = { version = "=0.28.0", features = ["bindgen-tests"] }
|
||||||
assert_matches = "1.5.0"
|
assert_matches = "1.5.0"
|
||||||
|
|
||||||
[profile.release-smaller]
|
[profile.release-smaller]
|
||||||
|
@ -5,16 +5,17 @@ namespace bdk {};
|
|||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface AddressError {
|
interface AddressParseError {
|
||||||
Base58();
|
Base58();
|
||||||
Bech32();
|
Bech32();
|
||||||
WitnessVersion(string error_message);
|
WitnessVersion(string error_message);
|
||||||
WitnessProgram(string error_message);
|
WitnessProgram(string error_message);
|
||||||
UncompressedPubkey();
|
UnknownHrp();
|
||||||
ExcessiveScriptSize();
|
LegacyAddressTooLong();
|
||||||
UnrecognizedScript();
|
InvalidBase58PayloadLength();
|
||||||
NetworkValidation(Network required, Network found, string address);
|
InvalidLegacyPrefix();
|
||||||
OtherAddressErr();
|
NetworkValidation();
|
||||||
|
OtherAddressParseErr();
|
||||||
};
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
@ -44,7 +45,7 @@ interface Bip39Error {
|
|||||||
[Error]
|
[Error]
|
||||||
interface CalculateFeeError {
|
interface CalculateFeeError {
|
||||||
MissingTxOut(sequence<OutPoint> out_points);
|
MissingTxOut(sequence<OutPoint> out_points);
|
||||||
NegativeFee(i64 fee);
|
NegativeFee(string amount);
|
||||||
};
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
@ -55,7 +56,6 @@ interface CannotConnectError {
|
|||||||
[Error]
|
[Error]
|
||||||
interface CreateTxError {
|
interface CreateTxError {
|
||||||
Descriptor(string error_message);
|
Descriptor(string error_message);
|
||||||
Persist(string error_message);
|
|
||||||
Policy(string error_message);
|
Policy(string error_message);
|
||||||
SpendingPolicyRequired(string kind);
|
SpendingPolicyRequired(string kind);
|
||||||
Version0();
|
Version0();
|
||||||
@ -63,7 +63,7 @@ interface CreateTxError {
|
|||||||
LockTime(string requested, string required);
|
LockTime(string requested, string required);
|
||||||
RbfSequence();
|
RbfSequence();
|
||||||
RbfSequenceCsv(string rbf, string csv);
|
RbfSequenceCsv(string rbf, string csv);
|
||||||
FeeTooLow(u64 required);
|
FeeTooLow(string required);
|
||||||
FeeRateTooLow(string required);
|
FeeRateTooLow(string required);
|
||||||
NoUtxosSelected();
|
NoUtxosSelected();
|
||||||
OutputBelowDustLimit(u64 index);
|
OutputBelowDustLimit(u64 index);
|
||||||
@ -92,6 +92,7 @@ interface DescriptorError {
|
|||||||
Pk(string error_message);
|
Pk(string error_message);
|
||||||
Miniscript(string error_message);
|
Miniscript(string error_message);
|
||||||
Hex(string error_message);
|
Hex(string error_message);
|
||||||
|
ExternalAndInternalAreTheSame();
|
||||||
};
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
@ -152,16 +153,21 @@ enum FeeRateError {
|
|||||||
"ArithmeticOverflow"
|
"ArithmeticOverflow"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
interface FromScriptError {
|
||||||
|
UnrecognizedScript();
|
||||||
|
WitnessProgram(string error_message);
|
||||||
|
WitnessVersion(string error_message);
|
||||||
|
OtherFromScriptErr();
|
||||||
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface ParseAmountError {
|
interface ParseAmountError {
|
||||||
Negative();
|
OutOfRange();
|
||||||
TooBig();
|
|
||||||
TooPrecise();
|
TooPrecise();
|
||||||
InvalidFormat();
|
MissingDigits();
|
||||||
InputTooLarge();
|
InputTooLarge();
|
||||||
InvalidCharacter(string error_message);
|
InvalidCharacter(string error_message);
|
||||||
UnknownDenomination(string error_message);
|
|
||||||
PossiblyConfusingDenomination(string error_message);
|
|
||||||
OtherParseAmountErr();
|
OtherParseAmountErr();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -170,12 +176,54 @@ interface PersistenceError {
|
|||||||
Write(string error_message);
|
Write(string error_message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
interface PsbtError {
|
||||||
|
InvalidMagic();
|
||||||
|
MissingUtxo();
|
||||||
|
InvalidSeparator();
|
||||||
|
PsbtUtxoOutOfBounds();
|
||||||
|
InvalidKey(string key);
|
||||||
|
InvalidProprietaryKey();
|
||||||
|
DuplicateKey(string key);
|
||||||
|
UnsignedTxHasScriptSigs();
|
||||||
|
UnsignedTxHasScriptWitnesses();
|
||||||
|
MustHaveUnsignedTx();
|
||||||
|
NoMorePairs();
|
||||||
|
UnexpectedUnsignedTx();
|
||||||
|
NonStandardSighashType(u32 sighash);
|
||||||
|
InvalidHash(string hash);
|
||||||
|
InvalidPreimageHashPair();
|
||||||
|
CombineInconsistentKeySources(string xpub);
|
||||||
|
ConsensusEncoding(string encoding_error);
|
||||||
|
NegativeFee();
|
||||||
|
FeeOverflow();
|
||||||
|
InvalidPublicKey(string error_message);
|
||||||
|
InvalidSecp256k1PublicKey(string secp256k1_error);
|
||||||
|
InvalidXOnlyPublicKey();
|
||||||
|
InvalidEcdsaSignature(string error_message);
|
||||||
|
InvalidTaprootSignature(string error_message);
|
||||||
|
InvalidControlBlock();
|
||||||
|
InvalidLeafVersion();
|
||||||
|
Taproot();
|
||||||
|
TapTree(string error_message);
|
||||||
|
XPubKey();
|
||||||
|
Version(string error_message);
|
||||||
|
PartialDataConsumption();
|
||||||
|
Io(string error_message);
|
||||||
|
OtherPsbtErr();
|
||||||
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface PsbtParseError {
|
interface PsbtParseError {
|
||||||
PsbtEncoding(string error_message);
|
PsbtEncoding(string error_message);
|
||||||
Base64Encoding(string error_message);
|
Base64Encoding(string error_message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
interface InspectError {
|
||||||
|
RequestAlreadyConsumed();
|
||||||
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface SignerError {
|
interface SignerError {
|
||||||
MissingKey();
|
MissingKey();
|
||||||
@ -189,11 +237,19 @@ interface SignerError {
|
|||||||
MissingHdKeypath();
|
MissingHdKeypath();
|
||||||
NonStandardSighash();
|
NonStandardSighash();
|
||||||
InvalidSighash();
|
InvalidSighash();
|
||||||
SighashError(string error_message);
|
SighashP2wpkh(string error_message);
|
||||||
|
SighashTaproot(string error_message);
|
||||||
|
TxInputsIndexError(string error_message);
|
||||||
MiniscriptPsbt(string error_message);
|
MiniscriptPsbt(string error_message);
|
||||||
External(string error_message);
|
External(string error_message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
interface SqliteError {
|
||||||
|
InvalidNetwork(Network expected, Network given);
|
||||||
|
Sqlite(string rusqlite_error);
|
||||||
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface TransactionError {
|
interface TransactionError {
|
||||||
Io();
|
Io();
|
||||||
@ -212,18 +268,14 @@ interface TxidParseError {
|
|||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface WalletCreationError {
|
interface WalletCreationError {
|
||||||
Io(string error_message);
|
Descriptor(string error_message);
|
||||||
InvalidMagicBytes(sequence<u8> got, sequence<u8> expected);
|
|
||||||
Descriptor();
|
|
||||||
Persist(string error_message);
|
|
||||||
NotInitialized();
|
|
||||||
LoadedGenesisDoesNotMatch(string expected, string got);
|
LoadedGenesisDoesNotMatch(string expected, string got);
|
||||||
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
||||||
LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain);
|
LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// bdk crate - types module
|
// bdk_wallet crate - types module
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
enum KeychainKind {
|
enum KeychainKind {
|
||||||
@ -274,12 +326,30 @@ dictionary CanonicalTx {
|
|||||||
ChainPosition chain_position;
|
ChainPosition chain_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FullScanRequest {};
|
interface FullScanRequest {
|
||||||
|
[Throws=InspectError]
|
||||||
|
FullScanRequest inspect_spks_for_all_keychains(FullScanScriptInspector inspector);
|
||||||
|
};
|
||||||
|
|
||||||
interface SyncRequest {};
|
interface SyncRequest {
|
||||||
|
[Throws=InspectError]
|
||||||
|
SyncRequest inspect_spks(SyncScriptInspector inspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
[Trait, WithForeign]
|
||||||
|
interface SyncScriptInspector {
|
||||||
|
void inspect(Script script, u64 total);
|
||||||
|
};
|
||||||
|
|
||||||
|
[Trait, WithForeign]
|
||||||
|
interface FullScanScriptInspector {
|
||||||
|
void inspect(KeychainKind keychain, u32 index, Script script);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ChangeSet {};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// bdk crate - wallet module
|
// bdk_wallet crate - wallet module
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
enum ChangeSpendPolicy {
|
enum ChangeSpendPolicy {
|
||||||
@ -290,24 +360,20 @@ enum ChangeSpendPolicy {
|
|||||||
|
|
||||||
interface Wallet {
|
interface Wallet {
|
||||||
[Throws=WalletCreationError]
|
[Throws=WalletCreationError]
|
||||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, string persistence_backend_path, Network network);
|
constructor(Descriptor descriptor, Descriptor change_descriptor, Network network);
|
||||||
|
|
||||||
[Name=new_no_persist, Throws=DescriptorError]
|
[Name=new_or_load, Throws=WalletCreationError]
|
||||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network);
|
constructor(Descriptor descriptor, Descriptor change_descriptor, ChangeSet? change_set, Network network);
|
||||||
|
|
||||||
[Throws=PersistenceError]
|
|
||||||
AddressInfo reveal_next_address(KeychainKind keychain);
|
AddressInfo reveal_next_address(KeychainKind keychain);
|
||||||
|
|
||||||
Network network();
|
Network network();
|
||||||
|
|
||||||
Balance get_balance();
|
Balance balance();
|
||||||
|
|
||||||
[Throws=CannotConnectError]
|
[Throws=CannotConnectError]
|
||||||
void apply_update(Update update);
|
void apply_update(Update update);
|
||||||
|
|
||||||
[Throws=PersistenceError]
|
|
||||||
boolean commit();
|
|
||||||
|
|
||||||
boolean is_mine([ByRef] Script script);
|
boolean is_mine([ByRef] Script script);
|
||||||
|
|
||||||
[Throws=SignerError]
|
[Throws=SignerError]
|
||||||
@ -321,7 +387,7 @@ interface Wallet {
|
|||||||
CanonicalTx? get_tx(string txid);
|
CanonicalTx? get_tx(string txid);
|
||||||
|
|
||||||
[Throws=CalculateFeeError]
|
[Throws=CalculateFeeError]
|
||||||
u64 calculate_fee([ByRef] Transaction tx);
|
Amount calculate_fee([ByRef] Transaction tx);
|
||||||
|
|
||||||
[Throws=CalculateFeeError]
|
[Throws=CalculateFeeError]
|
||||||
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
||||||
@ -333,6 +399,8 @@ interface Wallet {
|
|||||||
FullScanRequest start_full_scan();
|
FullScanRequest start_full_scan();
|
||||||
|
|
||||||
SyncRequest start_sync_with_revealed_spks();
|
SyncRequest start_sync_with_revealed_spks();
|
||||||
|
|
||||||
|
ChangeSet? take_staged();
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Update {};
|
interface Update {};
|
||||||
@ -360,7 +428,7 @@ interface TxBuilder {
|
|||||||
|
|
||||||
TxBuilder fee_rate([ByRef] FeeRate fee_rate);
|
TxBuilder fee_rate([ByRef] FeeRate fee_rate);
|
||||||
|
|
||||||
TxBuilder fee_absolute(u64 fee);
|
TxBuilder fee_absolute(Amount fee);
|
||||||
|
|
||||||
TxBuilder drain_wallet();
|
TxBuilder drain_wallet();
|
||||||
|
|
||||||
@ -385,10 +453,26 @@ interface BumpFeeTxBuilder {
|
|||||||
Psbt finish([ByRef] Wallet wallet);
|
Psbt finish([ByRef] Wallet wallet);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// bdk_sqlite crate
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface SqliteStore {
|
||||||
|
[Throws=SqliteError]
|
||||||
|
constructor(string path);
|
||||||
|
|
||||||
|
[Throws=SqliteError]
|
||||||
|
void write([ByRef] ChangeSet change_set);
|
||||||
|
|
||||||
|
[Throws=SqliteError]
|
||||||
|
ChangeSet? read();
|
||||||
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// bdk crate - descriptor module
|
// bdk crate - descriptor module
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[Traits=(Display)]
|
||||||
interface Mnemonic {
|
interface Mnemonic {
|
||||||
constructor(WordCount word_count);
|
constructor(WordCount word_count);
|
||||||
|
|
||||||
@ -397,8 +481,6 @@ interface Mnemonic {
|
|||||||
|
|
||||||
[Name=from_entropy, Throws=Bip39Error]
|
[Name=from_entropy, Throws=Bip39Error]
|
||||||
constructor(sequence<u8> entropy);
|
constructor(sequence<u8> entropy);
|
||||||
|
|
||||||
string as_string();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DerivationPath {
|
interface DerivationPath {
|
||||||
@ -438,6 +520,7 @@ interface DescriptorPublicKey {
|
|||||||
string as_string();
|
string as_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Traits=(Display)]
|
||||||
interface Descriptor {
|
interface Descriptor {
|
||||||
[Throws=DescriptorError]
|
[Throws=DescriptorError]
|
||||||
constructor(string descriptor, Network network);
|
constructor(string descriptor, Network network);
|
||||||
@ -466,9 +549,7 @@ interface Descriptor {
|
|||||||
[Name=new_bip86_public]
|
[Name=new_bip86_public]
|
||||||
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||||
|
|
||||||
string as_string();
|
string to_string_with_secret();
|
||||||
|
|
||||||
string as_string_private();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -521,7 +602,7 @@ dictionary SentAndReceivedValues {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// bdk crate - bitcoin re-exports
|
// bdk_wallet crate - bitcoin re-exports
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
interface Script {
|
interface Script {
|
||||||
@ -546,18 +627,18 @@ enum WordCount {
|
|||||||
"Words24",
|
"Words24",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Traits=(Display)]
|
||||||
interface Address {
|
interface Address {
|
||||||
[Throws=AddressError]
|
[Throws=AddressParseError]
|
||||||
constructor(string address, Network network);
|
constructor(string address, Network network);
|
||||||
|
|
||||||
Network network();
|
[Name=from_script, Throws=FromScriptError]
|
||||||
|
constructor(Script script, Network network);
|
||||||
|
|
||||||
Script script_pubkey();
|
Script script_pubkey();
|
||||||
|
|
||||||
string to_qr_uri();
|
string to_qr_uri();
|
||||||
|
|
||||||
string as_string();
|
|
||||||
|
|
||||||
boolean is_valid_for_network(Network network);
|
boolean is_valid_for_network(Network network);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -565,7 +646,7 @@ interface Transaction {
|
|||||||
[Throws=TransactionError]
|
[Throws=TransactionError]
|
||||||
constructor(sequence<u8> transaction_bytes);
|
constructor(sequence<u8> transaction_bytes);
|
||||||
|
|
||||||
string txid();
|
string compute_txid();
|
||||||
|
|
||||||
u64 total_size();
|
u64 total_size();
|
||||||
|
|
||||||
@ -598,6 +679,14 @@ interface Psbt {
|
|||||||
|
|
||||||
[Throws=ExtractTxError]
|
[Throws=ExtractTxError]
|
||||||
Transaction extract_tx();
|
Transaction extract_tx();
|
||||||
|
|
||||||
|
[Throws=PsbtError]
|
||||||
|
u64 fee();
|
||||||
|
|
||||||
|
[Throws=PsbtError]
|
||||||
|
Psbt combine(Psbt other);
|
||||||
|
|
||||||
|
string json_serialize();
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary OutPoint {
|
dictionary OutPoint {
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
use crate::error::{AddressError, FeeRateError, PsbtParseError, TransactionError};
|
use crate::error::{
|
||||||
|
AddressParseError, FeeRateError, FromScriptError, PsbtError, PsbtParseError, TransactionError,
|
||||||
|
};
|
||||||
|
|
||||||
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
use bdk_bitcoind_rpc::bitcoincore_rpc::jsonrpc::serde_json;
|
||||||
use bdk::bitcoin::amount::ParseAmountError;
|
use bdk_wallet::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
use bdk_wallet::bitcoin::amount::ParseAmountError;
|
||||||
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
|
use bdk_wallet::bitcoin::consensus::encode::serialize;
|
||||||
use bdk::bitcoin::consensus::encode::serialize;
|
use bdk_wallet::bitcoin::consensus::Decodable;
|
||||||
use bdk::bitcoin::consensus::Decodable;
|
use bdk_wallet::bitcoin::io::Cursor;
|
||||||
use bdk::bitcoin::psbt::ExtractTxError;
|
use bdk_wallet::bitcoin::psbt::ExtractTxError;
|
||||||
use bdk::bitcoin::Address as BdkAddress;
|
use bdk_wallet::bitcoin::Address as BdkAddress;
|
||||||
use bdk::bitcoin::Amount as BdkAmount;
|
use bdk_wallet::bitcoin::Amount as BdkAmount;
|
||||||
use bdk::bitcoin::FeeRate as BdkFeeRate;
|
use bdk_wallet::bitcoin::FeeRate as BdkFeeRate;
|
||||||
use bdk::bitcoin::Network;
|
use bdk_wallet::bitcoin::Network;
|
||||||
use bdk::bitcoin::OutPoint as BdkOutPoint;
|
use bdk_wallet::bitcoin::OutPoint as BdkOutPoint;
|
||||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
|
||||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||||
use bdk::bitcoin::TxIn as BdkTxIn;
|
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||||
use bdk::bitcoin::Txid;
|
use bdk_wallet::bitcoin::TxIn as BdkTxIn;
|
||||||
|
use bdk_wallet::bitcoin::TxOut as BdkTxOut;
|
||||||
|
use bdk_wallet::bitcoin::Txid;
|
||||||
|
|
||||||
use std::io::Cursor;
|
use std::fmt::Display;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@ -79,15 +84,17 @@ impl From<BdkScriptBuf> for Script {
|
|||||||
pub struct Address(BdkAddress<NetworkChecked>);
|
pub struct Address(BdkAddress<NetworkChecked>);
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
pub fn new(address: String, network: Network) -> Result<Self, AddressError> {
|
pub fn new(address: String, network: Network) -> Result<Self, AddressParseError> {
|
||||||
let parsed_address = address.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()?;
|
let parsed_address = address.parse::<bdk_wallet::bitcoin::Address<NetworkUnchecked>>()?;
|
||||||
let network_checked_address = parsed_address.require_network(network)?;
|
let network_checked_address = parsed_address.require_network(network)?;
|
||||||
|
|
||||||
Ok(Address(network_checked_address))
|
Ok(Address(network_checked_address))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn network(&self) -> Network {
|
pub fn from_script(script: Arc<Script>, network: Network) -> Result<Self, FromScriptError> {
|
||||||
*self.0.network()
|
let address = BdkAddress::from_script(&script.0.clone(), network)?;
|
||||||
|
|
||||||
|
Ok(Address(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn script_pubkey(&self) -> Arc<Script> {
|
pub fn script_pubkey(&self) -> Arc<Script> {
|
||||||
@ -98,10 +105,6 @@ impl Address {
|
|||||||
self.0.to_qr_uri()
|
self.0.to_qr_uri()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> String {
|
|
||||||
self.0.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid_for_network(&self, network: Network) -> bool {
|
pub fn is_valid_for_network(&self, network: Network) -> bool {
|
||||||
let address_str = self.0.to_string();
|
let address_str = self.0.to_string();
|
||||||
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
|
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
|
||||||
@ -112,6 +115,12 @@ impl Address {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Address {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Address> for BdkAddress {
|
impl From<Address> for BdkAddress {
|
||||||
fn from(address: Address) -> Self {
|
fn from(address: Address) -> Self {
|
||||||
address.0
|
address.0
|
||||||
@ -134,8 +143,8 @@ impl Transaction {
|
|||||||
Ok(Transaction(tx))
|
Ok(Transaction(tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn txid(&self) -> String {
|
pub fn compute_txid(&self) -> String {
|
||||||
self.0.txid().to_string()
|
self.0.compute_txid().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn weight(&self) -> u64 {
|
pub fn weight(&self) -> u64 {
|
||||||
@ -219,6 +228,27 @@ impl Psbt {
|
|||||||
let transaction: Transaction = tx.into();
|
let transaction: Transaction = tx.into();
|
||||||
Ok(Arc::new(transaction))
|
Ok(Arc::new(transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fee(&self) -> Result<u64, PsbtError> {
|
||||||
|
self.0
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.fee()
|
||||||
|
.map(|fee| fee.to_sat())
|
||||||
|
.map_err(PsbtError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn combine(&self, other: Arc<Psbt>) -> Result<Arc<Psbt>, PsbtError> {
|
||||||
|
let mut original_psbt = self.0.lock().unwrap().clone();
|
||||||
|
let other_psbt = other.0.lock().unwrap().clone();
|
||||||
|
original_psbt.combine(other_psbt)?;
|
||||||
|
Ok(Arc::new(Psbt(Mutex::new(original_psbt))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn json_serialize(&self) -> String {
|
||||||
|
let psbt = self.0.lock().unwrap();
|
||||||
|
serde_json::to_string(psbt.deref()).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BdkPsbt> for Psbt {
|
impl From<BdkPsbt> for Psbt {
|
||||||
@ -289,7 +319,7 @@ impl From<&BdkTxOut> for TxOut {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FeeRate(pub BdkFeeRate);
|
pub struct FeeRate(pub(crate) BdkFeeRate);
|
||||||
|
|
||||||
impl FeeRate {
|
impl FeeRate {
|
||||||
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
||||||
@ -342,11 +372,6 @@ mod tests {
|
|||||||
docs_address_testnet.is_valid_for_network(Network::Regtest),
|
docs_address_testnet.is_valid_for_network(Network::Regtest),
|
||||||
"Address should be valid for Regtest"
|
"Address should be valid for Regtest"
|
||||||
);
|
);
|
||||||
assert_ne!(
|
|
||||||
docs_address_testnet.network(),
|
|
||||||
Network::Bitcoin,
|
|
||||||
"Address should not be parsed as Bitcoin"
|
|
||||||
);
|
|
||||||
|
|
||||||
let docs_address_mainnet_str = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf";
|
let docs_address_mainnet_str = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf";
|
||||||
let docs_address_mainnet =
|
let docs_address_mainnet =
|
||||||
@ -355,21 +380,6 @@ mod tests {
|
|||||||
docs_address_mainnet.is_valid_for_network(Network::Bitcoin),
|
docs_address_mainnet.is_valid_for_network(Network::Bitcoin),
|
||||||
"Address should be valid for Bitcoin"
|
"Address should be valid for Bitcoin"
|
||||||
);
|
);
|
||||||
assert_ne!(
|
|
||||||
docs_address_mainnet.network(),
|
|
||||||
Network::Testnet,
|
|
||||||
"Address should not be valid for Testnet"
|
|
||||||
);
|
|
||||||
assert_ne!(
|
|
||||||
docs_address_mainnet.network(),
|
|
||||||
Network::Signet,
|
|
||||||
"Address should not be valid for Signet"
|
|
||||||
);
|
|
||||||
assert_ne!(
|
|
||||||
docs_address_mainnet.network(),
|
|
||||||
Network::Regtest,
|
|
||||||
"Address should not be valid for Regtest"
|
|
||||||
);
|
|
||||||
|
|
||||||
// ====Bech32====
|
// ====Bech32====
|
||||||
|
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
use crate::error::DescriptorError;
|
use crate::error::DescriptorError;
|
||||||
use crate::keys::DescriptorPublicKey;
|
use crate::keys::DescriptorPublicKey;
|
||||||
use crate::keys::DescriptorSecretKey;
|
use crate::keys::DescriptorSecretKey;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use bdk::bitcoin::bip32::Fingerprint;
|
use bdk_wallet::bitcoin::bip32::Fingerprint;
|
||||||
use bdk::bitcoin::key::Secp256k1;
|
use bdk_wallet::bitcoin::key::Secp256k1;
|
||||||
use bdk::bitcoin::Network;
|
use bdk_wallet::bitcoin::Network;
|
||||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
use bdk_wallet::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||||
use bdk::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
use bdk_wallet::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||||
use bdk::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
use bdk_wallet::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||||
use bdk::template::{
|
use bdk_wallet::template::{
|
||||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
||||||
DescriptorTemplate,
|
DescriptorTemplate,
|
||||||
};
|
};
|
||||||
use bdk::KeychainKind;
|
use bdk_wallet::KeychainKind;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@ -260,14 +261,16 @@ impl Descriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn as_string_private(&self) -> String {
|
pub(crate) fn to_string_with_secret(&self) -> String {
|
||||||
let descriptor = &self.extended_descriptor;
|
let descriptor = &self.extended_descriptor;
|
||||||
let key_map = &self.key_map;
|
let key_map = &self.key_map;
|
||||||
descriptor.to_string_with_secret(key_map)
|
descriptor.to_string_with_secret(key_map)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn as_string(&self) -> String {
|
impl Display for Descriptor {
|
||||||
self.extended_descriptor.to_string()
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.extended_descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,6 +278,8 @@ impl Descriptor {
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
|
use bdk_wallet::bitcoin::Network;
|
||||||
|
use bdk_wallet::KeychainKind;
|
||||||
|
|
||||||
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
||||||
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
||||||
@ -319,10 +324,10 @@ mod test {
|
|||||||
let template_private_86 =
|
let template_private_86 =
|
||||||
Descriptor::new_bip86(&master, KeychainKind::External, Network::Testnet);
|
Descriptor::new_bip86(&master, KeychainKind::External, Network::Testnet);
|
||||||
// the extended public keys are the same when creating them manually as they are with the templates
|
// the extended public keys are the same when creating them manually as they are with the templates
|
||||||
println!("Template 49: {}", template_private_49.as_string());
|
println!("Template 49: {}", template_private_49);
|
||||||
println!("Template 44: {}", template_private_44.as_string());
|
println!("Template 44: {}", template_private_44);
|
||||||
println!("Template 84: {}", template_private_84.as_string());
|
println!("Template 84: {}", template_private_84);
|
||||||
println!("Template 86: {}", template_private_86.as_string());
|
println!("Template 86: {}", template_private_86);
|
||||||
let template_public_44 = Descriptor::new_bip44_public(
|
let template_public_44 = Descriptor::new_bip44_public(
|
||||||
&handmade_public_44,
|
&handmade_public_44,
|
||||||
"d1d04177".to_string(),
|
"d1d04177".to_string(),
|
||||||
@ -347,43 +352,43 @@ mod test {
|
|||||||
KeychainKind::External,
|
KeychainKind::External,
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
);
|
);
|
||||||
println!("Template public 49: {}", template_public_49.as_string());
|
println!("Template public 49: {}", template_public_49);
|
||||||
println!("Template public 44: {}", template_public_44.as_string());
|
println!("Template public 44: {}", template_public_44);
|
||||||
println!("Template public 84: {}", template_public_84.as_string());
|
println!("Template public 84: {}", template_public_84);
|
||||||
println!("Template public 86: {}", template_public_86.as_string());
|
println!("Template public 86: {}", template_public_86);
|
||||||
// when using a public key, both as_string and as_string_private return the same string
|
// when using a public key, both to_string and as_string_private return the same string
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_public_44.as_string_private(),
|
template_public_44.to_string_with_secret(),
|
||||||
template_public_44.as_string()
|
template_public_44.to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_public_49.as_string_private(),
|
template_public_49.to_string_with_secret(),
|
||||||
template_public_49.as_string()
|
template_public_49.to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_public_84.as_string_private(),
|
template_public_84.to_string_with_secret(),
|
||||||
template_public_84.as_string()
|
template_public_84.to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_public_86.as_string_private(),
|
template_public_86.to_string_with_secret(),
|
||||||
template_public_86.as_string()
|
template_public_86.to_string()
|
||||||
);
|
);
|
||||||
// when using as_string on a private key, we get the same result as when using it on a public key
|
// when using to_string on a private key, we get the same result as when using it on a public key
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_private_44.as_string(),
|
template_private_44.to_string(),
|
||||||
template_public_44.as_string()
|
template_public_44.to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_private_49.as_string(),
|
template_private_49.to_string(),
|
||||||
template_public_49.as_string()
|
template_public_49.to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_private_84.as_string(),
|
template_private_84.to_string(),
|
||||||
template_public_84.as_string()
|
template_public_84.to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template_private_86.as_string(),
|
template_private_86.to_string(),
|
||||||
template_public_86.as_string()
|
template_public_86.to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -3,23 +3,29 @@ use crate::error::ElectrumError;
|
|||||||
use crate::types::{FullScanRequest, SyncRequest};
|
use crate::types::{FullScanRequest, SyncRequest};
|
||||||
use crate::wallet::Update;
|
use crate::wallet::Update;
|
||||||
|
|
||||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
use bdk_electrum::BdkElectrumClient as BdkBdkElectrumClient;
|
||||||
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
use bdk_electrum::{ElectrumFullScanResult, ElectrumSyncResult};
|
||||||
use bdk::chain::spk_client::FullScanResult as BdkFullScanResult;
|
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||||
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
|
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||||
use bdk::chain::spk_client::SyncResult as BdkSyncResult;
|
use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||||
use bdk::KeychainKind;
|
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||||
use bdk_electrum::electrum_client::{Client as BdkBlockingClient, ElectrumApi};
|
use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult;
|
||||||
use bdk_electrum::{ElectrumExt, ElectrumFullScanResult, ElectrumSyncResult};
|
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||||
|
use bdk_wallet::KeychainKind;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct ElectrumClient(BdkBlockingClient);
|
// NOTE: We are keeping our naming convention where the alias of the inner type is the Rust type
|
||||||
|
// prefixed with `Bdk`. In this case the inner type is `BdkElectrumClient`, so the alias is
|
||||||
|
// funnily enough named `BdkBdkElectrumClient`.
|
||||||
|
pub struct ElectrumClient(BdkBdkElectrumClient<bdk_electrum::electrum_client::Client>);
|
||||||
|
|
||||||
impl ElectrumClient {
|
impl ElectrumClient {
|
||||||
pub fn new(url: String) -> Result<Self, ElectrumError> {
|
pub fn new(url: String) -> Result<Self, ElectrumError> {
|
||||||
let client = BdkBlockingClient::new(url.as_str())?;
|
let inner_client: bdk_electrum::electrum_client::Client =
|
||||||
|
bdk_electrum::electrum_client::Client::new(url.as_str())?;
|
||||||
|
let client = BdkBdkElectrumClient::new(inner_client);
|
||||||
Ok(Self(client))
|
Ok(Self(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +53,7 @@ impl ElectrumClient {
|
|||||||
let full_scan_result: BdkFullScanResult<KeychainKind> =
|
let full_scan_result: BdkFullScanResult<KeychainKind> =
|
||||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||||
|
|
||||||
let update = bdk::wallet::Update {
|
let update = BdkUpdate {
|
||||||
last_active_indices: full_scan_result.last_active_indices,
|
last_active_indices: full_scan_result.last_active_indices,
|
||||||
graph: full_scan_result.graph_update,
|
graph: full_scan_result.graph_update,
|
||||||
chain: Some(full_scan_result.chain_update),
|
chain: Some(full_scan_result.chain_update),
|
||||||
@ -76,7 +82,7 @@ impl ElectrumClient {
|
|||||||
let sync_result: BdkSyncResult =
|
let sync_result: BdkSyncResult =
|
||||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||||
|
|
||||||
let update = bdk::wallet::Update {
|
let update = BdkUpdate {
|
||||||
last_active_indices: BTreeMap::default(),
|
last_active_indices: BTreeMap::default(),
|
||||||
graph: sync_result.graph_update,
|
graph: sync_result.graph_update,
|
||||||
chain: Some(sync_result.chain_update),
|
chain: Some(sync_result.chain_update),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,17 +2,18 @@ use crate::bitcoin::Transaction;
|
|||||||
use crate::error::EsploraError;
|
use crate::error::EsploraError;
|
||||||
use crate::types::{FullScanRequest, SyncRequest};
|
use crate::types::{FullScanRequest, SyncRequest};
|
||||||
use crate::wallet::Update;
|
use crate::wallet::Update;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
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_esplora::esplora_client::{BlockingClient, Builder};
|
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||||
use bdk_esplora::EsploraExt;
|
use bdk_esplora::EsploraExt;
|
||||||
|
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||||
|
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||||
|
use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||||
|
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||||
|
use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult;
|
||||||
|
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||||
|
use bdk_wallet::KeychainKind;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct EsploraClient(BlockingClient);
|
pub struct EsploraClient(BlockingClient);
|
||||||
@ -41,7 +42,7 @@ impl EsploraClient {
|
|||||||
self.0
|
self.0
|
||||||
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
|
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
|
||||||
|
|
||||||
let update = bdk::wallet::Update {
|
let update = BdkUpdate {
|
||||||
last_active_indices: result.last_active_indices,
|
last_active_indices: result.last_active_indices,
|
||||||
graph: result.graph_update,
|
graph: result.graph_update,
|
||||||
chain: Some(result.chain_update),
|
chain: Some(result.chain_update),
|
||||||
@ -65,7 +66,7 @@ impl EsploraClient {
|
|||||||
|
|
||||||
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
|
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
|
||||||
|
|
||||||
let update = bdk::wallet::Update {
|
let update = BdkUpdate {
|
||||||
last_active_indices: BTreeMap::default(),
|
last_active_indices: BTreeMap::default(),
|
||||||
graph: result.graph_update,
|
graph: result.graph_update,
|
||||||
chain: Some(result.chain_update),
|
chain: Some(result.chain_update),
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
use crate::error::{Bip32Error, Bip39Error, DescriptorKeyError};
|
use crate::error::{Bip32Error, Bip39Error, DescriptorKeyError};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use bdk::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
use bdk_wallet::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||||
use bdk::bitcoin::key::Secp256k1;
|
use bdk_wallet::bitcoin::key::Secp256k1;
|
||||||
use bdk::bitcoin::secp256k1::rand;
|
use bdk_wallet::bitcoin::secp256k1::rand;
|
||||||
use bdk::bitcoin::secp256k1::rand::Rng;
|
use bdk_wallet::bitcoin::secp256k1::rand::Rng;
|
||||||
use bdk::bitcoin::Network;
|
use bdk_wallet::bitcoin::Network;
|
||||||
use bdk::keys::bip39::WordCount;
|
use bdk_wallet::keys::bip39::WordCount;
|
||||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
use bdk_wallet::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||||
use bdk::keys::{
|
use bdk_wallet::keys::{
|
||||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||||
};
|
};
|
||||||
use bdk::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
use bdk_wallet::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
||||||
use bdk::miniscript::BareCtx;
|
use bdk_wallet::miniscript::BareCtx;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
pub(crate) struct Mnemonic(pub(crate) BdkMnemonic);
|
pub(crate) struct Mnemonic(BdkMnemonic);
|
||||||
|
|
||||||
impl Mnemonic {
|
impl Mnemonic {
|
||||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||||
@ -44,9 +45,11 @@ impl Mnemonic {
|
|||||||
.map(Mnemonic)
|
.map(Mnemonic)
|
||||||
.map_err(Bip39Error::from)
|
.map_err(Bip39Error::from)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn as_string(&self) -> String {
|
impl Display for Mnemonic {
|
||||||
self.0.to_string()
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +227,9 @@ impl DescriptorPublicKey {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
|
||||||
// use bdk::bitcoin::hashes::hex::ToHex;
|
|
||||||
use crate::error::DescriptorKeyError;
|
use crate::error::DescriptorKeyError;
|
||||||
use bdk::bitcoin::Network;
|
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||||
|
use bdk_wallet::bitcoin::Network;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn get_inner() -> DescriptorSecretKey {
|
fn get_inner() -> DescriptorSecretKey {
|
||||||
|
@ -4,6 +4,7 @@ mod electrum;
|
|||||||
mod error;
|
mod error;
|
||||||
mod esplora;
|
mod esplora;
|
||||||
mod keys;
|
mod keys;
|
||||||
|
mod store;
|
||||||
mod types;
|
mod types;
|
||||||
mod wallet;
|
mod wallet;
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ use crate::bitcoin::TxIn;
|
|||||||
use crate::bitcoin::TxOut;
|
use crate::bitcoin::TxOut;
|
||||||
use crate::descriptor::Descriptor;
|
use crate::descriptor::Descriptor;
|
||||||
use crate::electrum::ElectrumClient;
|
use crate::electrum::ElectrumClient;
|
||||||
use crate::error::AddressError;
|
use crate::error::AddressParseError;
|
||||||
use crate::error::Bip32Error;
|
use crate::error::Bip32Error;
|
||||||
use crate::error::Bip39Error;
|
use crate::error::Bip39Error;
|
||||||
use crate::error::CalculateFeeError;
|
use crate::error::CalculateFeeError;
|
||||||
@ -30,10 +31,14 @@ use crate::error::ElectrumError;
|
|||||||
use crate::error::EsploraError;
|
use crate::error::EsploraError;
|
||||||
use crate::error::ExtractTxError;
|
use crate::error::ExtractTxError;
|
||||||
use crate::error::FeeRateError;
|
use crate::error::FeeRateError;
|
||||||
|
use crate::error::FromScriptError;
|
||||||
|
use crate::error::InspectError;
|
||||||
use crate::error::ParseAmountError;
|
use crate::error::ParseAmountError;
|
||||||
use crate::error::PersistenceError;
|
use crate::error::PersistenceError;
|
||||||
|
use crate::error::PsbtError;
|
||||||
use crate::error::PsbtParseError;
|
use crate::error::PsbtParseError;
|
||||||
use crate::error::SignerError;
|
use crate::error::SignerError;
|
||||||
|
use crate::error::SqliteError;
|
||||||
use crate::error::TransactionError;
|
use crate::error::TransactionError;
|
||||||
use crate::error::TxidParseError;
|
use crate::error::TxidParseError;
|
||||||
use crate::error::WalletCreationError;
|
use crate::error::WalletCreationError;
|
||||||
@ -42,23 +47,27 @@ use crate::keys::DerivationPath;
|
|||||||
use crate::keys::DescriptorPublicKey;
|
use crate::keys::DescriptorPublicKey;
|
||||||
use crate::keys::DescriptorSecretKey;
|
use crate::keys::DescriptorSecretKey;
|
||||||
use crate::keys::Mnemonic;
|
use crate::keys::Mnemonic;
|
||||||
|
use crate::store::SqliteStore;
|
||||||
use crate::types::AddressInfo;
|
use crate::types::AddressInfo;
|
||||||
use crate::types::Balance;
|
use crate::types::Balance;
|
||||||
use crate::types::CanonicalTx;
|
use crate::types::CanonicalTx;
|
||||||
use crate::types::ChainPosition;
|
use crate::types::ChainPosition;
|
||||||
|
use crate::types::ChangeSet;
|
||||||
use crate::types::FullScanRequest;
|
use crate::types::FullScanRequest;
|
||||||
|
use crate::types::FullScanScriptInspector;
|
||||||
use crate::types::LocalOutput;
|
use crate::types::LocalOutput;
|
||||||
use crate::types::ScriptAmount;
|
use crate::types::ScriptAmount;
|
||||||
use crate::types::SyncRequest;
|
use crate::types::SyncRequest;
|
||||||
|
use crate::types::SyncScriptInspector;
|
||||||
use crate::wallet::BumpFeeTxBuilder;
|
use crate::wallet::BumpFeeTxBuilder;
|
||||||
use crate::wallet::SentAndReceivedValues;
|
use crate::wallet::SentAndReceivedValues;
|
||||||
use crate::wallet::TxBuilder;
|
use crate::wallet::TxBuilder;
|
||||||
use crate::wallet::Update;
|
use crate::wallet::Update;
|
||||||
use crate::wallet::Wallet;
|
use crate::wallet::Wallet;
|
||||||
|
|
||||||
use bdk::bitcoin::Network;
|
use bdk_wallet::bitcoin::Network;
|
||||||
use bdk::keys::bip39::WordCount;
|
use bdk_wallet::keys::bip39::WordCount;
|
||||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
|
||||||
use bdk::KeychainKind;
|
use bdk_wallet::KeychainKind;
|
||||||
|
|
||||||
uniffi::include_scaffolding!("bdk");
|
uniffi::include_scaffolding!("bdk");
|
||||||
|
39
bdk-ffi/src/store.rs
Normal file
39
bdk-ffi/src/store.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use crate::error::SqliteError;
|
||||||
|
use crate::types::ChangeSet;
|
||||||
|
|
||||||
|
use bdk_sqlite::rusqlite::Connection;
|
||||||
|
use bdk_sqlite::{Store as BdkSqliteStore, Store};
|
||||||
|
use bdk_wallet::chain::ConfirmationTimeHeightAnchor;
|
||||||
|
use bdk_wallet::KeychainKind;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
|
pub struct SqliteStore(Mutex<BdkSqliteStore<KeychainKind, ConfirmationTimeHeightAnchor>>);
|
||||||
|
|
||||||
|
impl SqliteStore {
|
||||||
|
pub fn new(path: String) -> Result<Self, SqliteError> {
|
||||||
|
let connection = Connection::open(path)?;
|
||||||
|
let db = Store::new(connection)?;
|
||||||
|
Ok(Self(Mutex::new(db)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_store(
|
||||||
|
&self,
|
||||||
|
) -> MutexGuard<BdkSqliteStore<KeychainKind, ConfirmationTimeHeightAnchor>> {
|
||||||
|
self.0.lock().expect("sqlite store")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, changeset: &ChangeSet) -> Result<(), SqliteError> {
|
||||||
|
self.get_store()
|
||||||
|
.write(&changeset.0)
|
||||||
|
.map_err(SqliteError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self) -> Result<Option<Arc<ChangeSet>>, SqliteError> {
|
||||||
|
self.get_store()
|
||||||
|
.read()
|
||||||
|
.map_err(SqliteError::from)
|
||||||
|
.map(|optional_bdk_change_set| optional_bdk_change_set.map(ChangeSet::from))
|
||||||
|
.map(|optional_change_set| optional_change_set.map(Arc::new))
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,20 @@
|
|||||||
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
|
|
||||||
|
|
||||||
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::AddressInfo as BdkAddressInfo;
|
|
||||||
use bdk::wallet::Balance as BdkBalance;
|
|
||||||
use bdk::KeychainKind;
|
|
||||||
use bdk::LocalOutput as BdkLocalOutput;
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use crate::bitcoin::Amount;
|
use crate::bitcoin::Amount;
|
||||||
|
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
|
||||||
|
use crate::InspectError;
|
||||||
|
|
||||||
|
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||||
|
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||||
|
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||||
|
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||||
|
use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
|
||||||
|
use bdk_wallet::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor};
|
||||||
|
use bdk_wallet::wallet::AddressInfo as BdkAddressInfo;
|
||||||
|
use bdk_wallet::wallet::Balance as BdkBalance;
|
||||||
|
use bdk_wallet::KeychainKind;
|
||||||
|
use bdk_wallet::LocalOutput as BdkLocalOutput;
|
||||||
|
|
||||||
|
use bdk_electrum::bdk_chain::CombinedChangeSet;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ChainPosition {
|
pub enum ChainPosition {
|
||||||
@ -24,12 +27,8 @@ pub struct CanonicalTx {
|
|||||||
pub chain_position: ChainPosition,
|
pub chain_position: ChainPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHeightAnchor>>
|
impl From<BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>> for CanonicalTx {
|
||||||
for CanonicalTx
|
fn from(tx: BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>) -> Self {
|
||||||
{
|
|
||||||
fn from(
|
|
||||||
tx: BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHeightAnchor>,
|
|
||||||
) -> Self {
|
|
||||||
let chain_position = match tx.chain_position {
|
let chain_position = match tx.chain_position {
|
||||||
BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed {
|
BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed {
|
||||||
height: anchor.confirmation_height,
|
height: anchor.confirmation_height,
|
||||||
@ -88,6 +87,14 @@ impl From<BdkBalance> for Balance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ChangeSet(pub(crate) CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>);
|
||||||
|
|
||||||
|
impl From<CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>> for ChangeSet {
|
||||||
|
fn from(change_set: CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>) -> Self {
|
||||||
|
ChangeSet(change_set)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LocalOutput {
|
pub struct LocalOutput {
|
||||||
pub outpoint: OutPoint,
|
pub outpoint: OutPoint,
|
||||||
pub txout: TxOut,
|
pub txout: TxOut,
|
||||||
@ -112,6 +119,55 @@ impl From<BdkLocalOutput> for LocalOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback for the FullScanRequest
|
||||||
|
pub trait FullScanScriptInspector: Sync + Send {
|
||||||
|
fn inspect(&self, keychain: KeychainKind, index: u32, script: Arc<Script>);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback for the SyncRequest
|
||||||
|
pub trait SyncScriptInspector: Sync + Send {
|
||||||
|
fn inspect(&self, script: Arc<Script>, total: u64);
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
|
pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
|
||||||
|
|
||||||
pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest>>);
|
pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest>>);
|
||||||
|
|
||||||
|
impl SyncRequest {
|
||||||
|
pub fn inspect_spks(
|
||||||
|
&self,
|
||||||
|
inspector: Arc<dyn SyncScriptInspector>,
|
||||||
|
) -> Result<Arc<Self>, InspectError> {
|
||||||
|
let mut guard = self.0.lock().unwrap();
|
||||||
|
if let Some(sync_request) = guard.take() {
|
||||||
|
let total = sync_request.spks.len() as u64;
|
||||||
|
let sync_request = sync_request.inspect_spks(move |spk| {
|
||||||
|
inspector.inspect(Arc::new(BdkScriptBuf::from(spk).into()), total)
|
||||||
|
});
|
||||||
|
Ok(Arc::new(SyncRequest(Mutex::new(Some(sync_request)))))
|
||||||
|
} else {
|
||||||
|
Err(InspectError::RequestAlreadyConsumed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FullScanRequest {
|
||||||
|
pub fn inspect_spks_for_all_keychains(
|
||||||
|
&self,
|
||||||
|
inspector: Arc<dyn FullScanScriptInspector>,
|
||||||
|
) -> Result<Arc<Self>, InspectError> {
|
||||||
|
let mut guard = self.0.lock().unwrap();
|
||||||
|
if let Some(full_scan_request) = guard.take() {
|
||||||
|
let inspector = Arc::new(inspector);
|
||||||
|
let full_scan_request =
|
||||||
|
full_scan_request.inspect_spks_for_all_keychains(move |k, spk_i, script| {
|
||||||
|
inspector.inspect(k, spk_i, Arc::new(BdkScriptBuf::from(script).into()))
|
||||||
|
});
|
||||||
|
Ok(Arc::new(FullScanRequest(Mutex::new(Some(
|
||||||
|
full_scan_request,
|
||||||
|
)))))
|
||||||
|
} else {
|
||||||
|
Err(InspectError::RequestAlreadyConsumed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,30 +2,29 @@ use crate::bitcoin::Amount;
|
|||||||
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
|
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
|
||||||
use crate::descriptor::Descriptor;
|
use crate::descriptor::Descriptor;
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
CalculateFeeError, CannotConnectError, CreateTxError, DescriptorError, PersistenceError,
|
CalculateFeeError, CannotConnectError, CreateTxError, SignerError, TxidParseError,
|
||||||
SignerError, TxidParseError, WalletCreationError,
|
WalletCreationError,
|
||||||
};
|
};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
AddressInfo, Balance, CanonicalTx, FullScanRequest, LocalOutput, ScriptAmount, SyncRequest,
|
AddressInfo, Balance, CanonicalTx, ChangeSet, FullScanRequest, LocalOutput, ScriptAmount,
|
||||||
|
SyncRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bdk::bitcoin::amount::Amount as BdkAmount;
|
use bdk_wallet::bitcoin::amount::Amount as BdkAmount;
|
||||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
use bdk_wallet::bitcoin::Network;
|
||||||
use bdk::bitcoin::Network;
|
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
|
||||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||||
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
use bdk_wallet::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
use bdk_wallet::chain::{CombinedChangeSet, ConfirmationTimeHeightAnchor};
|
||||||
use bdk::wallet::{ChangeSet, Update as BdkUpdate};
|
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
|
||||||
use bdk::Wallet as BdkWallet;
|
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||||
use bdk::{KeychainKind, SignOptions};
|
use bdk_wallet::Wallet as BdkWallet;
|
||||||
use bdk_file_store::Store;
|
use bdk_wallet::{KeychainKind, SignOptions};
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
const MAGIC_BYTES: &[u8] = "bdkffi".as_bytes();
|
|
||||||
|
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
inner_mutex: Mutex<BdkWallet>,
|
inner_mutex: Mutex<BdkWallet>,
|
||||||
}
|
}
|
||||||
@ -33,32 +32,30 @@ pub struct Wallet {
|
|||||||
impl Wallet {
|
impl Wallet {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
descriptor: Arc<Descriptor>,
|
descriptor: Arc<Descriptor>,
|
||||||
change_descriptor: Option<Arc<Descriptor>>,
|
change_descriptor: Arc<Descriptor>,
|
||||||
persistence_backend_path: String,
|
|
||||||
network: Network,
|
network: Network,
|
||||||
) -> Result<Self, WalletCreationError> {
|
) -> Result<Self, WalletCreationError> {
|
||||||
let descriptor = descriptor.as_string_private();
|
let descriptor = descriptor.to_string_with_secret();
|
||||||
let change_descriptor = change_descriptor.map(|d| d.as_string_private());
|
let change_descriptor = change_descriptor.to_string_with_secret();
|
||||||
let db = Store::<ChangeSet>::open_or_create_new(MAGIC_BYTES, persistence_backend_path)?;
|
let wallet: BdkWallet = BdkWallet::new(&descriptor, &change_descriptor, network)?;
|
||||||
|
|
||||||
let wallet: BdkWallet =
|
|
||||||
BdkWallet::new_or_load(&descriptor, change_descriptor.as_ref(), db, network)?;
|
|
||||||
|
|
||||||
Ok(Wallet {
|
Ok(Wallet {
|
||||||
inner_mutex: Mutex::new(wallet),
|
inner_mutex: Mutex::new(wallet),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_no_persist(
|
pub fn new_or_load(
|
||||||
descriptor: Arc<Descriptor>,
|
descriptor: Arc<Descriptor>,
|
||||||
change_descriptor: Option<Arc<Descriptor>>,
|
change_descriptor: Arc<Descriptor>,
|
||||||
|
change_set: Option<Arc<ChangeSet>>,
|
||||||
network: Network,
|
network: Network,
|
||||||
) -> Result<Self, DescriptorError> {
|
) -> Result<Self, WalletCreationError> {
|
||||||
let descriptor = descriptor.as_string_private();
|
let descriptor = descriptor.to_string_with_secret();
|
||||||
let change_descriptor = change_descriptor.map(|d| d.as_string_private());
|
let change_descriptor = change_descriptor.to_string_with_secret();
|
||||||
|
let change_set: Option<CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>> =
|
||||||
|
change_set.map(|cs| cs.0.clone());
|
||||||
let wallet: BdkWallet =
|
let wallet: BdkWallet =
|
||||||
BdkWallet::new_no_persist(&descriptor, change_descriptor.as_ref(), network)?;
|
BdkWallet::new_or_load(&descriptor, &change_descriptor, change_set, network)?;
|
||||||
|
|
||||||
Ok(Wallet {
|
Ok(Wallet {
|
||||||
inner_mutex: Mutex::new(wallet),
|
inner_mutex: Mutex::new(wallet),
|
||||||
@ -69,16 +66,8 @@ impl Wallet {
|
|||||||
self.inner_mutex.lock().expect("wallet")
|
self.inner_mutex.lock().expect("wallet")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reveal_next_address(
|
pub fn reveal_next_address(&self, keychain_kind: KeychainKind) -> AddressInfo {
|
||||||
&self,
|
self.get_wallet().reveal_next_address(keychain_kind).into()
|
||||||
keychain_kind: KeychainKind,
|
|
||||||
) -> Result<AddressInfo, PersistenceError> {
|
|
||||||
self.get_wallet()
|
|
||||||
.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<(), CannotConnectError> {
|
pub fn apply_update(&self, update: Arc<Update>) -> Result<(), CannotConnectError> {
|
||||||
@ -87,20 +76,12 @@ impl Wallet {
|
|||||||
.map_err(CannotConnectError::from)
|
.map_err(CannotConnectError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn network(&self) -> Network {
|
||||||
self.get_wallet().network()
|
self.get_wallet().network()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_balance(&self) -> Balance {
|
pub fn balance(&self) -> Balance {
|
||||||
let bdk_balance = self.get_wallet().get_balance();
|
let bdk_balance = self.get_wallet().balance();
|
||||||
Balance::from(bdk_balance)
|
Balance::from(bdk_balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,9 +121,11 @@ impl Wallet {
|
|||||||
Ok(self.get_wallet().get_tx(txid).map(|tx| tx.into()))
|
Ok(self.get_wallet().get_tx(txid).map(|tx| tx.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
|
pub fn calculate_fee(&self, tx: &Transaction) -> Result<Arc<Amount>, CalculateFeeError> {
|
||||||
self.get_wallet()
|
self.get_wallet()
|
||||||
.calculate_fee(&tx.into())
|
.calculate_fee(&tx.into())
|
||||||
|
.map(Amount::from)
|
||||||
|
.map(Arc::new)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +153,12 @@ impl Wallet {
|
|||||||
let request = self.get_wallet().start_sync_with_revealed_spks();
|
let request = self.get_wallet().start_sync_with_revealed_spks();
|
||||||
Arc::new(SyncRequest(Mutex::new(Some(request))))
|
Arc::new(SyncRequest(Mutex::new(Some(request))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take_staged(&self) -> Option<Arc<ChangeSet>> {
|
||||||
|
self.get_wallet()
|
||||||
|
.take_staged()
|
||||||
|
.map(|change_set| Arc::new(change_set.into()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SentAndReceivedValues {
|
pub struct SentAndReceivedValues {
|
||||||
@ -187,7 +176,7 @@ pub struct TxBuilder {
|
|||||||
pub(crate) change_policy: ChangeSpendPolicy,
|
pub(crate) change_policy: ChangeSpendPolicy,
|
||||||
pub(crate) manually_selected_only: bool,
|
pub(crate) manually_selected_only: bool,
|
||||||
pub(crate) fee_rate: Option<FeeRate>,
|
pub(crate) fee_rate: Option<FeeRate>,
|
||||||
pub(crate) fee_absolute: Option<u64>,
|
pub(crate) fee_absolute: Option<Arc<Amount>>,
|
||||||
pub(crate) drain_wallet: bool,
|
pub(crate) drain_wallet: bool,
|
||||||
pub(crate) drain_to: Option<BdkScriptBuf>,
|
pub(crate) drain_to: Option<BdkScriptBuf>,
|
||||||
pub(crate) rbf: Option<RbfValue>,
|
pub(crate) rbf: Option<RbfValue>,
|
||||||
@ -296,7 +285,7 @@ impl TxBuilder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
|
pub(crate) fn fee_absolute(&self, fee_amount: Arc<Amount>) -> Arc<Self> {
|
||||||
Arc::new(TxBuilder {
|
Arc::new(TxBuilder {
|
||||||
fee_absolute: Some(fee_amount),
|
fee_absolute: Some(fee_amount),
|
||||||
..self.clone()
|
..self.clone()
|
||||||
@ -356,8 +345,8 @@ impl TxBuilder {
|
|||||||
if let Some(fee_rate) = &self.fee_rate {
|
if let Some(fee_rate) = &self.fee_rate {
|
||||||
tx_builder.fee_rate(fee_rate.0);
|
tx_builder.fee_rate(fee_rate.0);
|
||||||
}
|
}
|
||||||
if let Some(fee_amount) = self.fee_absolute {
|
if let Some(fee_amount) = &self.fee_absolute {
|
||||||
tx_builder.fee_absolute(fee_amount);
|
tx_builder.fee_absolute(fee_amount.0);
|
||||||
}
|
}
|
||||||
if self.drain_wallet {
|
if self.drain_wallet {
|
||||||
tx_builder.drain_wallet();
|
tx_builder.drain_wallet();
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import bdk
|
import BitcoinDevKit
|
||||||
|
|
||||||
let network = Network.testnet
|
let network = Network.testnet
|
||||||
|
@ -5,8 +5,6 @@ cdylib_name = "bdkffi"
|
|||||||
[bindings.python]
|
[bindings.python]
|
||||||
cdylib_name = "bdkffi"
|
cdylib_name = "bdkffi"
|
||||||
|
|
||||||
[bindings.ruby]
|
|
||||||
cdylib_name = "bdkffi"
|
|
||||||
|
|
||||||
[bindings.swift]
|
[bindings.swift]
|
||||||
|
module_name = "BitcoinDevKit"
|
||||||
cdylib_name = "bdkffi"
|
cdylib_name = "bdkffi"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536m
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
libraryVersion=1.0.0-alpha.10-SNAPSHOT
|
libraryVersion=1.0.0-alpha.12-SNAPSHOT
|
||||||
|
@ -24,13 +24,16 @@ java {
|
|||||||
|
|
||||||
// This block ensures that the tests that require access to a blockchain are not
|
// This block ensures that the tests that require access to a blockchain are not
|
||||||
// run if the -P excludeConnectedTests flag is passed to gradle.
|
// run if the -P excludeConnectedTests flag is passed to gradle.
|
||||||
// This ensures our CI runs are not fickle by not requiring access to testnet.
|
// This ensures our CI runs are not fickle by not requiring access to testnet or signet.
|
||||||
// This is a workaround until we have a proper regtest setup for the CI.
|
// This is a workaround until we have a proper regtest setup for the CI.
|
||||||
// Note that the command in the CI is ./gradlew test -P excludeConnectedTests
|
// Note that the command in the CI is ./gradlew test -P excludeConnectedTests
|
||||||
tasks.test {
|
tasks.test {
|
||||||
if (project.hasProperty("excludeConnectedTests")) {
|
if (project.hasProperty("excludeConnectedTests")) {
|
||||||
exclude("**/LiveWalletTest.class")
|
exclude("**/LiveElectrumClientTest.class")
|
||||||
|
exclude("**/LiveMemoryWalletTest.class")
|
||||||
|
exclude("**/LiveTransactionTest.class")
|
||||||
exclude("**/LiveTxBuilderTest.class")
|
exclude("**/LiveTxBuilderTest.class")
|
||||||
|
exclude("**/LiveWalletTest.class")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,29 +5,33 @@ import kotlin.test.Test
|
|||||||
private const val SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
private const val SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||||
|
|
||||||
class LiveElectrumClientTest {
|
class LiveElectrumClientTest {
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSyncedBalance() {
|
fun testSyncedBalance() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
"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 electrumClient: ElectrumClient = ElectrumClient(SIGNET_ELECTRUM_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = electrumClient.fullScan(fullScanRequest, 10uL, 10uL, false)
|
val update = electrumClient.fullScan(fullScanRequest, 10uL, 10uL, false)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Transactions count: ${wallet.transactions().count()}")
|
println("Transactions count: ${wallet.transactions().count()}")
|
||||||
val transactions = wallet.transactions().take(3)
|
val transactions = wallet.transactions().take(3)
|
||||||
for (tx in transactions) {
|
for (tx in transactions) {
|
||||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||||
println("Transaction: ${tx.transaction.txid()}")
|
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||||
println("Received ${sentAndReceived.received.toSat()}")
|
println("Received ${sentAndReceived.received.toSat()}")
|
||||||
}
|
}
|
||||||
|
@ -6,31 +6,62 @@ private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
|||||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
class LiveMemoryWalletTest {
|
class LiveMemoryWalletTest {
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSyncedBalance() {
|
fun testSyncedBalance() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
val descriptor: Descriptor = Descriptor(
|
||||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
Network.SIGNET
|
Network.SIGNET
|
||||||
)
|
)
|
||||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.SIGNET)
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Transactions count: ${wallet.transactions().count()}")
|
println("Transactions count: ${wallet.transactions().count()}")
|
||||||
val transactions = wallet.transactions().take(3)
|
val transactions = wallet.transactions().take(3)
|
||||||
for (tx in transactions) {
|
for (tx in transactions) {
|
||||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||||
println("Transaction: ${tx.transaction.txid()}")
|
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||||
println("Received ${sentAndReceived.received.toSat()}")
|
println("Received ${sentAndReceived.received.toSat()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
|
fun testScriptInspector() {
|
||||||
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
|
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
|
|
||||||
|
val scriptInspector: FullScriptInspector = FullScriptInspector()
|
||||||
|
val fullScanRequest: FullScanRequest = wallet.startFullScan().inspectSpksForAllKeychains(scriptInspector)
|
||||||
|
val update = esploraClient.fullScan(fullScanRequest, 21uL, 1uL)
|
||||||
|
|
||||||
|
wallet.applyUpdate(update)
|
||||||
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
|
|
||||||
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullScriptInspector: FullScanScriptInspector {
|
||||||
|
override fun inspect(keychain: KeychainKind, index: UInt, script: Script){
|
||||||
|
println("Inspecting index $index for keychain $keychain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,27 +6,35 @@ private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
|||||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
class LiveTransactionTests {
|
class LiveTransactionTests {
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSyncedBalance() {
|
fun testSyncedBalance() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
val descriptor: Descriptor = Descriptor(
|
||||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
Network.SIGNET
|
Network.SIGNET
|
||||||
)
|
)
|
||||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.SIGNET)
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Wallet balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Wallet balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
val transaction: Transaction = wallet.transactions().first().transaction
|
val transaction: Transaction = wallet.transactions().first().transaction
|
||||||
println("First transaction:")
|
println("First transaction:")
|
||||||
println("Txid: ${transaction.txid()}")
|
println("Txid: ${transaction.computeTxid()}")
|
||||||
println("Version: ${transaction.version()}")
|
println("Version: ${transaction.version()}")
|
||||||
println("Total size: ${transaction.totalSize()}")
|
println("Total size: ${transaction.totalSize()}")
|
||||||
println("Vsize: ${transaction.vsize()}")
|
println("Vsize: ${transaction.vsize()}")
|
||||||
|
@ -11,8 +11,16 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
|||||||
class LiveTxBuilderTest {
|
class LiveTxBuilderTest {
|
||||||
private val persistenceFilePath = run {
|
private val persistenceFilePath = run {
|
||||||
val currentDirectory = System.getProperty("user.dir")
|
val currentDirectory = System.getProperty("user.dir")
|
||||||
"$currentDirectory/bdk_persistence.db"
|
"$currentDirectory/bdk_persistence.sqlite"
|
||||||
}
|
}
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
|
||||||
@AfterTest
|
@AfterTest
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
@ -25,16 +33,15 @@ class LiveTxBuilderTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testTxBuilder() {
|
fun testTxBuilder() {
|
||||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"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.SIGNET)
|
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||||
@ -50,18 +57,15 @@ class LiveTxBuilderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun complexTxBuilder() {
|
fun complexTxBuilder() {
|
||||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
|
|
||||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.SIGNET)
|
|
||||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||||
|
@ -11,8 +11,16 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
|||||||
class LiveWalletTest {
|
class LiveWalletTest {
|
||||||
private val persistenceFilePath = run {
|
private val persistenceFilePath = run {
|
||||||
val currentDirectory = System.getProperty("user.dir")
|
val currentDirectory = System.getProperty("user.dir")
|
||||||
"$currentDirectory/bdk_persistence.db"
|
"$currentDirectory/bdk_persistence.sqlite"
|
||||||
}
|
}
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
|
||||||
@AfterTest
|
@AfterTest
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
@ -24,24 +32,22 @@ class LiveWalletTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSyncedBalance() {
|
fun testSyncedBalance() {
|
||||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
|
||||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Transactions count: ${wallet.transactions().count()}")
|
println("Transactions count: ${wallet.transactions().count()}")
|
||||||
val transactions = wallet.transactions().take(3)
|
val transactions = wallet.transactions().take(3)
|
||||||
for (tx in transactions) {
|
for (tx in transactions) {
|
||||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||||
println("Transaction: ${tx.transaction.txid()}")
|
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||||
println("Sent ${sentAndReceived.sent}")
|
println("Sent ${sentAndReceived.sent}")
|
||||||
println("Received ${sentAndReceived.received}")
|
println("Received ${sentAndReceived.received}")
|
||||||
}
|
}
|
||||||
@ -50,16 +56,15 @@ class LiveWalletTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testBroadcastTransaction() {
|
fun testBroadcastTransaction() {
|
||||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||||
wallet.applyUpdate(update)
|
wallet.applyUpdate(update)
|
||||||
wallet.commit()
|
println("Balance: ${wallet.balance().total.toSat()}")
|
||||||
println("Balance: ${wallet.getBalance().total.toSat()}")
|
|
||||||
|
|
||||||
assert(wallet.getBalance().total.toSat() > 0uL) {
|
assert(wallet.balance().total.toSat() > 0uL) {
|
||||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
"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.SIGNET)
|
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||||
@ -76,9 +81,9 @@ class LiveWalletTest {
|
|||||||
assertTrue(walletDidSign)
|
assertTrue(walletDidSign)
|
||||||
|
|
||||||
val tx: Transaction = psbt.extractTx()
|
val tx: Transaction = psbt.extractTx()
|
||||||
println("Txid is: ${tx.txid()}")
|
println("Txid is: ${tx.computeTxid()}")
|
||||||
|
|
||||||
val txFee: ULong = wallet.calculateFee(tx)
|
val txFee: Amount = wallet.calculateFee(tx)
|
||||||
println("Tx fee is: ${txFee}")
|
println("Tx fee is: ${txFee}")
|
||||||
|
|
||||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||||
|
@ -12,7 +12,7 @@ class OfflineDescriptorTest {
|
|||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||||
actual = descriptor.asString()
|
actual = descriptor.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.bitcoindevkit
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class OfflinePersistenceTest {
|
||||||
|
private val persistenceFilePath = run {
|
||||||
|
val currentDirectory = System.getProperty("user.dir")
|
||||||
|
val dbFileName = "pre_existing_wallet_persistence_test.sqlite"
|
||||||
|
"$currentDirectory/src/test/resources/$dbFileName"
|
||||||
|
}
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.SIGNET
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPersistence() {
|
||||||
|
val sqliteStore: SqliteStore = SqliteStore(persistenceFilePath)
|
||||||
|
val initialChangeSet: ChangeSet? = sqliteStore.read()
|
||||||
|
requireNotNull(initialChangeSet) { "ChangeSet should not be null after loading a valid database" }
|
||||||
|
|
||||||
|
val wallet: Wallet = Wallet.newOrLoad(
|
||||||
|
descriptor,
|
||||||
|
changeDescriptor,
|
||||||
|
initialChangeSet,
|
||||||
|
Network.SIGNET,
|
||||||
|
)
|
||||||
|
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||||
|
println("Address: $addressInfo")
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
expected = 7u,
|
||||||
|
actual = addressInfo.index,
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
expected = "tb1qan3lldunh37ma6c0afeywgjyjgnyc8uz975zl2",
|
||||||
|
actual = addressInfo.address.toString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -10,8 +10,16 @@ import kotlin.test.assertFalse
|
|||||||
class OfflineWalletTest {
|
class OfflineWalletTest {
|
||||||
private val persistenceFilePath = run {
|
private val persistenceFilePath = run {
|
||||||
val currentDirectory = System.getProperty("user.dir")
|
val currentDirectory = System.getProperty("user.dir")
|
||||||
"$currentDirectory/bdk_persistence.db"
|
"$currentDirectory/bdk_persistence.sqlite"
|
||||||
}
|
}
|
||||||
|
private val descriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
Network.TESTNET
|
||||||
|
)
|
||||||
|
private val changeDescriptor: Descriptor = Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
Network.TESTNET
|
||||||
|
)
|
||||||
|
|
||||||
@AfterTest
|
@AfterTest
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
@ -27,19 +35,14 @@ class OfflineWalletTest {
|
|||||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||||
|
|
||||||
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
assertTrue(descriptor.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNewAddress() {
|
fun testNewAddress() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
Network.TESTNET
|
|
||||||
)
|
|
||||||
val wallet: Wallet = Wallet(
|
val wallet: Wallet = Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
null,
|
changeDescriptor,
|
||||||
persistenceFilePath,
|
|
||||||
Network.TESTNET
|
Network.TESTNET
|
||||||
)
|
)
|
||||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||||
@ -50,27 +53,22 @@ class OfflineWalletTest {
|
|||||||
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
|
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||||
actual = addressInfo.address.asString()
|
actual = addressInfo.address.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBalance() {
|
fun testBalance() {
|
||||||
val descriptor: Descriptor = Descriptor(
|
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
Network.TESTNET
|
|
||||||
)
|
|
||||||
val wallet: Wallet = Wallet(
|
val wallet: Wallet = Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
null,
|
changeDescriptor,
|
||||||
persistenceFilePath,
|
|
||||||
Network.TESTNET
|
Network.TESTNET
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected = 0uL,
|
expected = 0uL,
|
||||||
actual = wallet.getBalance().total.toSat()
|
actual = wallet.balance().total.toSat()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -18,7 +18,7 @@ import bdkpython as bdk
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="bdkpython",
|
name="bdkpython",
|
||||||
version="1.0.0a10.dev",
|
version="1.0.0a12.dev",
|
||||||
description="The Python language bindings for the Bitcoin Development Kit",
|
description="The Python language bindings for the Bitcoin Development Kit",
|
||||||
long_description=LONG_DESCRIPTION,
|
long_description=LONG_DESCRIPTION,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
@ -5,11 +5,20 @@ import os
|
|||||||
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||||
TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
|
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
bdk.Network.TESTNET
|
||||||
|
)
|
||||||
|
change_descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
bdk.Network.TESTNET
|
||||||
|
)
|
||||||
|
|
||||||
class LiveTxBuilderTest(unittest.TestCase):
|
class LiveTxBuilderTest(unittest.TestCase):
|
||||||
|
|
||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
if os.path.exists("./bdk_persistence.db"):
|
if os.path.exists("./bdk_persistence.sqlite"):
|
||||||
os.remove("./bdk_persistence.db")
|
os.remove("./bdk_persistence.sqlite")
|
||||||
|
|
||||||
def test_tx_builder(self):
|
def test_tx_builder(self):
|
||||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
@ -18,8 +27,7 @@ class LiveTxBuilderTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
wallet: bdk.Wallet = bdk.Wallet(
|
wallet: bdk.Wallet = bdk.Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
None,
|
change_descriptor,
|
||||||
"./bdk_persistence.db",
|
|
||||||
bdk.Network.SIGNET
|
bdk.Network.SIGNET
|
||||||
)
|
)
|
||||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||||
@ -30,12 +38,11 @@ class LiveTxBuilderTest(unittest.TestCase):
|
|||||||
parallel_requests=1
|
parallel_requests=1
|
||||||
)
|
)
|
||||||
wallet.apply_update(update)
|
wallet.apply_update(update)
|
||||||
wallet.commit()
|
|
||||||
|
|
||||||
self.assertGreater(
|
self.assertGreater(
|
||||||
wallet.get_balance().total.to_sat(),
|
wallet.balance().total.to_sat(),
|
||||||
0,
|
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."
|
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
recipient = bdk.Address(
|
recipient = bdk.Address(
|
||||||
@ -48,14 +55,9 @@ class LiveTxBuilderTest(unittest.TestCase):
|
|||||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||||
|
|
||||||
def complex_tx_builder(self):
|
def complex_tx_builder(self):
|
||||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
|
||||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
bdk.Network.SIGNET
|
|
||||||
)
|
|
||||||
wallet: bdk.Wallet = bdk.Wallet(
|
wallet: bdk.Wallet = bdk.Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
None,
|
change_descriptor,
|
||||||
"./bdk_persistence.db",
|
|
||||||
bdk.Network.SIGNET
|
bdk.Network.SIGNET
|
||||||
)
|
)
|
||||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||||
@ -66,12 +68,11 @@ class LiveTxBuilderTest(unittest.TestCase):
|
|||||||
parallel_requests=1
|
parallel_requests=1
|
||||||
)
|
)
|
||||||
wallet.apply_update(update)
|
wallet.apply_update(update)
|
||||||
wallet.commit()
|
|
||||||
|
|
||||||
self.assertGreater(
|
self.assertGreater(
|
||||||
wallet.get_balance().total.to_sat(),
|
wallet.balance().total.to_sat(),
|
||||||
0,
|
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."
|
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
recipient1 = bdk.Address(
|
recipient1 = bdk.Address(
|
||||||
|
@ -5,21 +5,25 @@ import os
|
|||||||
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||||
TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
|
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
bdk.Network.TESTNET
|
||||||
|
)
|
||||||
|
change_descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
bdk.Network.TESTNET
|
||||||
|
)
|
||||||
|
|
||||||
class LiveWalletTest(unittest.TestCase):
|
class LiveWalletTest(unittest.TestCase):
|
||||||
|
|
||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
if os.path.exists("./bdk_persistence.db"):
|
if os.path.exists("./bdk_persistence.sqlite"):
|
||||||
os.remove("./bdk_persistence.db")
|
os.remove("./bdk_persistence.sqlite")
|
||||||
|
|
||||||
def test_synced_balance(self):
|
def test_synced_balance(self):
|
||||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
|
||||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
bdk.Network.SIGNET
|
|
||||||
)
|
|
||||||
wallet: bdk.Wallet = bdk.Wallet(
|
wallet: bdk.Wallet = bdk.Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
None,
|
change_descriptor,
|
||||||
"./bdk_persistence.db",
|
|
||||||
bdk.Network.SIGNET
|
bdk.Network.SIGNET
|
||||||
)
|
)
|
||||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||||
@ -30,32 +34,26 @@ class LiveWalletTest(unittest.TestCase):
|
|||||||
parallel_requests=1
|
parallel_requests=1
|
||||||
)
|
)
|
||||||
wallet.apply_update(update)
|
wallet.apply_update(update)
|
||||||
wallet.commit()
|
|
||||||
|
|
||||||
self.assertGreater(
|
self.assertGreater(
|
||||||
wallet.get_balance().total.to_sat(),
|
wallet.balance().total.to_sat(),
|
||||||
0,
|
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."
|
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Transactions count: {len(wallet.transactions())}")
|
print(f"Transactions count: {len(wallet.transactions())}")
|
||||||
transactions = wallet.transactions()[:3]
|
transactions = wallet.transactions()[:3]
|
||||||
for tx in transactions:
|
for tx in transactions:
|
||||||
sent_and_received = wallet.sent_and_received(tx.transaction)
|
sent_and_received = wallet.sent_and_received(tx.transaction)
|
||||||
print(f"Transaction: {tx.transaction.txid()}")
|
print(f"Transaction: {tx.transaction.compute_txid()}")
|
||||||
print(f"Sent {sent_and_received.sent.to_sat()}")
|
print(f"Sent {sent_and_received.sent.to_sat()}")
|
||||||
print(f"Received {sent_and_received.received.to_sat()}")
|
print(f"Received {sent_and_received.received.to_sat()}")
|
||||||
|
|
||||||
|
|
||||||
def test_broadcast_transaction(self):
|
def test_broadcast_transaction(self):
|
||||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
|
||||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
bdk.Network.SIGNET
|
|
||||||
)
|
|
||||||
wallet: bdk.Wallet = bdk.Wallet(
|
wallet: bdk.Wallet = bdk.Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
None,
|
change_descriptor,
|
||||||
"./bdk_persistence.db",
|
|
||||||
bdk.Network.SIGNET
|
bdk.Network.SIGNET
|
||||||
)
|
)
|
||||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||||
@ -66,12 +64,11 @@ class LiveWalletTest(unittest.TestCase):
|
|||||||
parallel_requests=1
|
parallel_requests=1
|
||||||
)
|
)
|
||||||
wallet.apply_update(update)
|
wallet.apply_update(update)
|
||||||
wallet.commit()
|
|
||||||
|
|
||||||
self.assertGreater(
|
self.assertGreater(
|
||||||
wallet.get_balance().total.to_sat(),
|
wallet.balance().total.to_sat(),
|
||||||
0,
|
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."
|
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
recipient = bdk.Address(
|
recipient = bdk.Address(
|
||||||
@ -85,9 +82,9 @@ class LiveWalletTest(unittest.TestCase):
|
|||||||
walletDidSign = wallet.sign(psbt)
|
walletDidSign = wallet.sign(psbt)
|
||||||
self.assertTrue(walletDidSign)
|
self.assertTrue(walletDidSign)
|
||||||
tx = psbt.extract_tx()
|
tx = psbt.extract_tx()
|
||||||
print(f"Transaction Id: {tx.txid()}")
|
print(f"Transaction Id: {tx.compute_txid()}")
|
||||||
fee = wallet.calculate_fee(tx)
|
fee = wallet.calculate_fee(tx)
|
||||||
print(f"Transaction Fee: {fee}")
|
print(f"Transaction Fee: {fee.to_sat()}")
|
||||||
fee_rate = wallet.calculate_fee_rate(tx)
|
fee_rate = wallet.calculate_fee_rate(tx)
|
||||||
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB")
|
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB")
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class OfflineDescriptorTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
"tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||||
descriptor.as_string()
|
descriptor.__str__()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,21 +2,25 @@ import bdkpython as bdk
|
|||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
bdk.Network.TESTNET
|
||||||
|
)
|
||||||
|
change_descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||||
|
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
bdk.Network.TESTNET
|
||||||
|
)
|
||||||
|
|
||||||
class OfflineWalletTest(unittest.TestCase):
|
class OfflineWalletTest(unittest.TestCase):
|
||||||
|
|
||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
if os.path.exists("./bdk_persistence.db"):
|
if os.path.exists("./bdk_persistence.sqlite"):
|
||||||
os.remove("./bdk_persistence.db")
|
os.remove("./bdk_persistence.sqlite")
|
||||||
|
|
||||||
def test_new_address(self):
|
def test_new_address(self):
|
||||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
bdk.Network.TESTNET
|
|
||||||
)
|
|
||||||
wallet: Wallet = bdk.Wallet(
|
wallet: Wallet = bdk.Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
None,
|
change_descriptor,
|
||||||
"./bdk_persistence.db",
|
|
||||||
bdk.Network.TESTNET
|
bdk.Network.TESTNET
|
||||||
)
|
)
|
||||||
address_info: bdk.AddressInfo = wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL)
|
address_info: bdk.AddressInfo = wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL)
|
||||||
@ -26,21 +30,16 @@ class OfflineWalletTest(unittest.TestCase):
|
|||||||
self.assertFalse(address_info.address.is_valid_for_network(bdk.Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
|
self.assertFalse(address_info.address.is_valid_for_network(bdk.Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
|
||||||
self.assertFalse(address_info.address.is_valid_for_network(bdk.Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
self.assertFalse(address_info.address.is_valid_for_network(bdk.Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||||
|
|
||||||
self.assertEqual("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address_info.address.as_string())
|
self.assertEqual("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", address_info.address.__str__())
|
||||||
|
|
||||||
def test_balance(self):
|
def test_balance(self):
|
||||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
bdk.Network.TESTNET
|
|
||||||
)
|
|
||||||
wallet: bdk.Wallet = bdk.Wallet(
|
wallet: bdk.Wallet = bdk.Wallet(
|
||||||
descriptor,
|
descriptor,
|
||||||
None,
|
change_descriptor,
|
||||||
"./bdk_persistence.db",
|
|
||||||
bdk.Network.TESTNET
|
bdk.Network.TESTNET
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(wallet.get_balance().total.to_sat(), 0)
|
self.assertEqual(wallet.balance().total.to_sat(), 0)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -33,6 +33,10 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "BitcoinDevKitTests",
|
name: "BitcoinDevKitTests",
|
||||||
dependencies: ["BitcoinDevKit"]),
|
dependencies: ["BitcoinDevKit"],
|
||||||
|
resources: [
|
||||||
|
.copy("Resources/pre_existing_wallet_persistence_test.sqlite")
|
||||||
|
]
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -25,7 +25,7 @@ import BitcoinDevKit
|
|||||||
|
|
||||||
Swift Package Manager releases for `bdk-swift` are published to a separate repository (https://github.com/bitcoindevkit/bdk-swift), and that is where the releases are created for it.
|
Swift Package Manager releases for `bdk-swift` are published to a separate repository (https://github.com/bitcoindevkit/bdk-swift), and that is where the releases are created for it.
|
||||||
|
|
||||||
The `bdk-swift/build-local-swift.sh` script can be used instead to create a version of the project for local testing.
|
The `bdk-swift/build-xcframework.sh` script can be used instead to create a version of the project for local testing.
|
||||||
|
|
||||||
### How to test
|
### How to test
|
||||||
|
|
||||||
|
@ -4,15 +4,20 @@ import XCTest
|
|||||||
private let SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
private let SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||||
|
|
||||||
final class LiveElectrumClientTests: XCTestCase {
|
final class LiveElectrumClientTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
|
||||||
func testSyncedBalance() throws {
|
func testSyncedBalance() throws {
|
||||||
let descriptor = try Descriptor(
|
let wallet = try Wallet(
|
||||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
network: Network.signet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet.newNoPersist(
|
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
network: .signet
|
network: Network.signet
|
||||||
)
|
)
|
||||||
let electrumClient: ElectrumClient = try ElectrumClient(url: SIGNET_ELECTRUM_URL)
|
let electrumClient: ElectrumClient = try ElectrumClient(url: SIGNET_ELECTRUM_URL)
|
||||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
@ -23,11 +28,10 @@ final class LiveElectrumClientTests: XCTestCase {
|
|||||||
fetchPrevTxouts: false
|
fetchPrevTxouts: false
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
@ -36,7 +40,7 @@ final class LiveElectrumClientTests: XCTestCase {
|
|||||||
let transactions = wallet.transactions().prefix(3)
|
let transactions = wallet.transactions().prefix(3)
|
||||||
for tx in transactions {
|
for tx in transactions {
|
||||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||||
print("Transaction: \(tx.transaction.txid())")
|
print("Transaction: \(tx.transaction.computeTxid())")
|
||||||
print("Sent \(sentAndReceived.sent.toSat())")
|
print("Sent \(sentAndReceived.sent.toSat())")
|
||||||
print("Received \(sentAndReceived.received.toSat())")
|
print("Received \(sentAndReceived.received.toSat())")
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,19 @@ private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
|||||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
final class LiveMemoryWalletTests: XCTestCase {
|
final class LiveMemoryWalletTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
|
||||||
func testSyncedBalance() throws {
|
func testSyncedBalance() throws {
|
||||||
let descriptor = try Descriptor(
|
let wallet = try Wallet(
|
||||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
network: Network.signet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet.newNoPersist(
|
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
network: .signet
|
network: .signet
|
||||||
)
|
)
|
||||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
@ -23,11 +28,10 @@ final class LiveMemoryWalletTests: XCTestCase {
|
|||||||
parallelRequests: 1
|
parallelRequests: 1
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
@ -36,9 +40,41 @@ final class LiveMemoryWalletTests: XCTestCase {
|
|||||||
let transactions = wallet.transactions().prefix(3)
|
let transactions = wallet.transactions().prefix(3)
|
||||||
for tx in transactions {
|
for tx in transactions {
|
||||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||||
print("Transaction: \(tx.transaction.txid())")
|
print("Transaction: \(tx.transaction.computeTxid())")
|
||||||
print("Sent \(sentAndReceived.sent.toSat())")
|
print("Sent \(sentAndReceived.sent.toSat())")
|
||||||
print("Received \(sentAndReceived.received.toSat())")
|
print("Received \(sentAndReceived.received.toSat())")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testScriptInspector() throws {
|
||||||
|
let wallet = try Wallet(
|
||||||
|
descriptor: descriptor,
|
||||||
|
changeDescriptor: changeDescriptor,
|
||||||
|
network: .signet
|
||||||
|
)
|
||||||
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
|
let scriptInspector = FullScriptInspector()
|
||||||
|
let fullScanRequest = try wallet.startFullScan().inspectSpksForAllKeychains(inspector: scriptInspector)
|
||||||
|
|
||||||
|
let update = try esploraClient.fullScan(
|
||||||
|
fullScanRequest: fullScanRequest,
|
||||||
|
stopGap: 21,
|
||||||
|
parallelRequests: 1
|
||||||
|
)
|
||||||
|
try wallet.applyUpdate(update: update)
|
||||||
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
|
|
||||||
|
XCTAssertGreaterThan(
|
||||||
|
wallet.balance().total.toSat(),
|
||||||
|
UInt64(0),
|
||||||
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullScriptInspector: FullScanScriptInspector {
|
||||||
|
func inspect(keychain: KeychainKind, index: UInt32, script: Script) {
|
||||||
|
print("Inspecting index \(index) for keychain \(keychain)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,21 @@ import XCTest
|
|||||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
|
|
||||||
final class LiveTransactionTests: XCTestCase {
|
final class LiveTransactionTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
|
||||||
func testSyncedBalance() throws {
|
func testSyncedBalance() throws {
|
||||||
let descriptor = try Descriptor(
|
let wallet = try Wallet(
|
||||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
network: Network.signet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet.newNoPersist(
|
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
network: .signet
|
network: .signet
|
||||||
)
|
)
|
||||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
@ -23,11 +29,10 @@ final class LiveTransactionTests: XCTestCase {
|
|||||||
parallelRequests: 1
|
parallelRequests: 1
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
@ -37,7 +42,7 @@ final class LiveTransactionTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
print("First transaction:")
|
print("First transaction:")
|
||||||
print("Txid: \(transaction.txid())")
|
print("Txid: \(transaction.computeTxid())")
|
||||||
print("Version: \(transaction.version())")
|
print("Version: \(transaction.version())")
|
||||||
print("Total size: \(transaction.totalSize())")
|
print("Total size: \(transaction.totalSize())")
|
||||||
print("Vsize: \(transaction.vsize())")
|
print("Vsize: \(transaction.vsize())")
|
||||||
|
@ -5,13 +5,21 @@ private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
|||||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
final class LiveTxBuilderTests: XCTestCase {
|
final class LiveTxBuilderTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
var dbFilePath: URL!
|
var dbFilePath: URL!
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).db"
|
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).sqlite"
|
||||||
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
||||||
|
|
||||||
if fileManager.fileExists(atPath: dbFilePath.path) {
|
if fileManager.fileExists(atPath: dbFilePath.path) {
|
||||||
@ -27,14 +35,9 @@ final class LiveTxBuilderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testTxBuilder() throws {
|
func testTxBuilder() throws {
|
||||||
let descriptor = try Descriptor(
|
|
||||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
network: Network.signet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet(
|
let wallet = try Wallet(
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
persistenceBackendPath: dbFilePath.path,
|
|
||||||
network: .signet
|
network: .signet
|
||||||
)
|
)
|
||||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
@ -45,11 +48,10 @@ final class LiveTxBuilderTests: XCTestCase {
|
|||||||
parallelRequests: 1
|
parallelRequests: 1
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
@ -76,7 +78,6 @@ final class LiveTxBuilderTests: XCTestCase {
|
|||||||
let wallet = try Wallet(
|
let wallet = try Wallet(
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: changeDescriptor,
|
changeDescriptor: changeDescriptor,
|
||||||
persistenceBackendPath: dbFilePath.path,
|
|
||||||
network: .signet
|
network: .signet
|
||||||
)
|
)
|
||||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
@ -87,11 +88,10 @@ final class LiveTxBuilderTests: XCTestCase {
|
|||||||
parallelRequests: 1
|
parallelRequests: 1
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
|
@ -5,13 +5,21 @@ private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
|||||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||||
|
|
||||||
final class LiveWalletTests: XCTestCase {
|
final class LiveWalletTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
var dbFilePath: URL!
|
var dbFilePath: URL!
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).db"
|
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).sqlite"
|
||||||
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
||||||
|
|
||||||
if fileManager.fileExists(atPath: dbFilePath.path) {
|
if fileManager.fileExists(atPath: dbFilePath.path) {
|
||||||
@ -27,14 +35,9 @@ final class LiveWalletTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testSyncedBalance() throws {
|
func testSyncedBalance() throws {
|
||||||
let descriptor = try Descriptor(
|
|
||||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
network: Network.signet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet(
|
let wallet = try Wallet(
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
persistenceBackendPath: dbFilePath.path,
|
|
||||||
network: .signet
|
network: .signet
|
||||||
)
|
)
|
||||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
@ -45,11 +48,10 @@ final class LiveWalletTests: XCTestCase {
|
|||||||
parallelRequests: 1
|
parallelRequests: 1
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
@ -58,21 +60,16 @@ final class LiveWalletTests: XCTestCase {
|
|||||||
let transactions = wallet.transactions().prefix(3)
|
let transactions = wallet.transactions().prefix(3)
|
||||||
for tx in transactions {
|
for tx in transactions {
|
||||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||||
print("Transaction: \(tx.transaction.txid())")
|
print("Transaction: \(tx.transaction.computeTxid())")
|
||||||
print("Sent \(sentAndReceived.sent.toSat())")
|
print("Sent \(sentAndReceived.sent.toSat())")
|
||||||
print("Received \(sentAndReceived.received.toSat())")
|
print("Received \(sentAndReceived.received.toSat())")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBroadcastTransaction() throws {
|
func testBroadcastTransaction() throws {
|
||||||
let descriptor = try Descriptor(
|
|
||||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
|
||||||
network: Network.signet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet(
|
let wallet = try Wallet(
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
persistenceBackendPath: dbFilePath.path,
|
|
||||||
network: .signet
|
network: .signet
|
||||||
)
|
)
|
||||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||||
@ -83,16 +80,15 @@ final class LiveWalletTests: XCTestCase {
|
|||||||
parallelRequests: 1
|
parallelRequests: 1
|
||||||
)
|
)
|
||||||
try wallet.applyUpdate(update: update)
|
try wallet.applyUpdate(update: update)
|
||||||
let _ = try wallet.commit()
|
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||||
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
|
||||||
|
|
||||||
XCTAssertGreaterThan(
|
XCTAssertGreaterThan(
|
||||||
wallet.getBalance().total.toSat(),
|
wallet.balance().total.toSat(),
|
||||||
UInt64(0),
|
UInt64(0),
|
||||||
"Wallet must have positive balance, please send funds to \(address)"
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
)
|
)
|
||||||
|
|
||||||
print("Balance: \(wallet.getBalance().total)")
|
print("Balance: \(wallet.balance().total)")
|
||||||
|
|
||||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||||
let psbt: Psbt = try
|
let psbt: Psbt = try
|
||||||
@ -108,8 +104,8 @@ final class LiveWalletTests: XCTestCase {
|
|||||||
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
|
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
|
||||||
|
|
||||||
let tx: Transaction = try! psbt.extractTx()
|
let tx: Transaction = try! psbt.extractTx()
|
||||||
print(tx.txid())
|
print(tx.computeTxid())
|
||||||
let fee: UInt64 = try wallet.calculateFee(tx: tx)
|
let fee: Amount = try wallet.calculateFee(tx: tx)
|
||||||
print("Transaction Fee: \(fee)")
|
print("Transaction Fee: \(fee)")
|
||||||
let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx)
|
let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx)
|
||||||
print("Transaction Fee Rate: \(feeRate.toSatPerVbCeil()) sat/vB")
|
print("Transaction Fee Rate: \(feeRate.toSatPerVbCeil()) sat/vB")
|
||||||
|
@ -15,6 +15,6 @@ final class OfflineDescriptorTests: XCTestCase {
|
|||||||
network: Network.testnet
|
network: Network.testnet
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(descriptor.asString(), "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx")
|
XCTAssertEqual(descriptor.description, "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import BitcoinDevKit
|
||||||
|
|
||||||
|
final class OfflinePersistenceTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
var dbFilePath: URL!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
guard let resourceUrl = Bundle.module.url(
|
||||||
|
forResource: "pre_existing_wallet_persistence_test",
|
||||||
|
withExtension: "sqlite"
|
||||||
|
) else {
|
||||||
|
print("error finding resourceURL")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dbFilePath = resourceUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPersistence() throws {
|
||||||
|
let sqliteStore = try! SqliteStore(path: dbFilePath.path)
|
||||||
|
let initialChangeSet = try! sqliteStore.read()
|
||||||
|
let wallet = try Wallet.newOrLoad(
|
||||||
|
descriptor: descriptor,
|
||||||
|
changeDescriptor: changeDescriptor,
|
||||||
|
changeSet: initialChangeSet,
|
||||||
|
network: .signet
|
||||||
|
)
|
||||||
|
let nextAddress: AddressInfo = wallet.revealNextAddress(keychain: KeychainKind.external)
|
||||||
|
print("Address: \(nextAddress)")
|
||||||
|
|
||||||
|
XCTAssertTrue(nextAddress.address.description == "tb1qan3lldunh37ma6c0afeywgjyjgnyc8uz975zl2")
|
||||||
|
XCTAssertTrue(nextAddress.index == 7)
|
||||||
|
}
|
||||||
|
}
|
@ -2,13 +2,21 @@ import XCTest
|
|||||||
@testable import BitcoinDevKit
|
@testable import BitcoinDevKit
|
||||||
|
|
||||||
final class OfflineWalletTests: XCTestCase {
|
final class OfflineWalletTests: XCTestCase {
|
||||||
|
private let descriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
private let changeDescriptor = try! Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
var dbFilePath: URL!
|
var dbFilePath: URL!
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).db"
|
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).sqlite"
|
||||||
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
||||||
|
|
||||||
if fileManager.fileExists(atPath: dbFilePath.path) {
|
if fileManager.fileExists(atPath: dbFilePath.path) {
|
||||||
@ -24,17 +32,12 @@ final class OfflineWalletTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testNewAddress() throws {
|
func testNewAddress() throws {
|
||||||
let descriptor: Descriptor = try Descriptor(
|
|
||||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
network: Network.testnet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet(
|
let wallet = try Wallet(
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
persistenceBackendPath: dbFilePath.path,
|
|
||||||
network: .testnet
|
network: .testnet
|
||||||
)
|
)
|
||||||
let addressInfo: AddressInfo = try wallet.revealNextAddress(keychain: KeychainKind.external)
|
let addressInfo: AddressInfo = wallet.revealNextAddress(keychain: KeychainKind.external)
|
||||||
|
|
||||||
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.testnet),
|
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.testnet),
|
||||||
"Address is not valid for testnet network")
|
"Address is not valid for testnet network")
|
||||||
@ -45,21 +48,16 @@ final class OfflineWalletTests: XCTestCase {
|
|||||||
XCTAssertFalse(addressInfo.address.isValidForNetwork(network: Network.bitcoin),
|
XCTAssertFalse(addressInfo.address.isValidForNetwork(network: Network.bitcoin),
|
||||||
"Address is valid for bitcoin network, but it shouldn't be")
|
"Address is valid for bitcoin network, but it shouldn't be")
|
||||||
|
|
||||||
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
|
XCTAssertEqual(addressInfo.address.description, "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBalance() throws {
|
func testBalance() throws {
|
||||||
let descriptor: Descriptor = try Descriptor(
|
|
||||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
|
||||||
network: Network.testnet
|
|
||||||
)
|
|
||||||
let wallet = try Wallet(
|
let wallet = try Wallet(
|
||||||
descriptor: descriptor,
|
descriptor: descriptor,
|
||||||
changeDescriptor: nil,
|
changeDescriptor: changeDescriptor,
|
||||||
persistenceBackendPath: dbFilePath.path,
|
|
||||||
network: .testnet
|
network: .testnet
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(wallet.getBalance().total.toSat(), 0)
|
XCTAssertEqual(wallet.balance().total.toSat(), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>AvailableLibraries</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>LibraryIdentifier</key>
|
|
||||||
<string>macos-arm64_x86_64</string>
|
|
||||||
<key>LibraryPath</key>
|
|
||||||
<string>bdkFFI.framework</string>
|
|
||||||
<key>SupportedArchitectures</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
<string>x86_64</string>
|
|
||||||
</array>
|
|
||||||
<key>SupportedPlatform</key>
|
|
||||||
<string>macos</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>12.0</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LibraryIdentifier</key>
|
|
||||||
<string>ios-arm64_x86_64-simulator</string>
|
|
||||||
<key>LibraryPath</key>
|
|
||||||
<string>bdkFFI.framework</string>
|
|
||||||
<key>SupportedArchitectures</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
<string>x86_64</string>
|
|
||||||
</array>
|
|
||||||
<key>SupportedPlatform</key>
|
|
||||||
<string>ios</string>
|
|
||||||
<key>SupportedPlatformVariant</key>
|
|
||||||
<string>simulator</string>
|
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>15.0</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LibraryIdentifier</key>
|
|
||||||
<string>ios-arm64</string>
|
|
||||||
<key>LibraryPath</key>
|
|
||||||
<string>bdkFFI.framework</string>
|
|
||||||
<key>SupportedArchitectures</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
</array>
|
|
||||||
<key>SupportedPlatform</key>
|
|
||||||
<string>ios</string>
|
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>15.0</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>XFWK</string>
|
|
||||||
<key>XCFrameworkFormatVersion</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1,4 +0,0 @@
|
|||||||
// This is the "umbrella header" for our combined Rust code library.
|
|
||||||
// It needs to import all of the individual headers.
|
|
||||||
|
|
||||||
#import "bdkFFI.h"
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.bitcoindevkit.bdkFFI</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>bdkFFI</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1.0.0</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0.0</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>bdkFFI</string>
|
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>100</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1,6 +0,0 @@
|
|||||||
framework module bdkFFI {
|
|
||||||
umbrella header "bdkFFI-umbrella.h"
|
|
||||||
|
|
||||||
export *
|
|
||||||
module * { export * }
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
// This is the "umbrella header" for our combined Rust code library.
|
|
||||||
// It needs to import all of the individual headers.
|
|
||||||
|
|
||||||
#import "bdkFFI.h"
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.bitcoindevkit.bdkFFI</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>bdkFFI</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1.0.0</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0.0</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>bdkFFI</string>
|
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>15.0</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1,6 +0,0 @@
|
|||||||
framework module bdkFFI {
|
|
||||||
umbrella header "bdkFFI-umbrella.h"
|
|
||||||
|
|
||||||
export *
|
|
||||||
module * { export * }
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
// This is the "umbrella header" for our combined Rust code library.
|
|
||||||
// It needs to import all of the individual headers.
|
|
||||||
|
|
||||||
#import "bdkFFI.h"
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.bitcoindevkit.bdkFFI</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>bdkFFI</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1.0.0</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0.0</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>bdkFFI</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>12.0</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1,6 +0,0 @@
|
|||||||
framework module bdkFFI {
|
|
||||||
umbrella header "bdkFFI-umbrella.h"
|
|
||||||
|
|
||||||
export *
|
|
||||||
module * { export * }
|
|
||||||
}
|
|
@ -3,6 +3,16 @@
|
|||||||
# The results of this script can be used for locally testing your SPM package adding a local package
|
# The results of this script can be used for locally testing your SPM package adding a local package
|
||||||
# to your application pointing at the bdk-swift directory.
|
# to your application pointing at the bdk-swift directory.
|
||||||
|
|
||||||
|
HEADERPATH="Sources/BitcoinDevKit/BitcoinDevKitFFI.h"
|
||||||
|
MODMAPPATH="Sources/BitcoinDevKit/BitcoinDevKitFFI.modulemap"
|
||||||
|
TARGETDIR="../bdk-ffi/target"
|
||||||
|
OUTDIR="."
|
||||||
|
RELDIR="release-smaller"
|
||||||
|
NAME="bdkffi"
|
||||||
|
STATIC_LIB_NAME="lib${NAME}.a"
|
||||||
|
NEW_HEADER_DIR="../bdk-ffi/target/include"
|
||||||
|
|
||||||
|
# set required rust version and install component and targets
|
||||||
rustup default 1.77.1
|
rustup default 1.77.1
|
||||||
rustup component add rust-src
|
rustup component add rust-src
|
||||||
rustup target add aarch64-apple-ios # iOS arm64
|
rustup target add aarch64-apple-ios # iOS arm64
|
||||||
@ -13,26 +23,38 @@ rustup target add x86_64-apple-darwin # mac x86_64
|
|||||||
|
|
||||||
cd ../bdk-ffi/ || exit
|
cd ../bdk-ffi/ || exit
|
||||||
|
|
||||||
|
# build bdk-ffi rust lib for apple targets
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
|
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-ios
|
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-ios
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios
|
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 build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios-sim
|
||||||
|
|
||||||
|
# build bdk-ffi Swift bindings and put in bdk-swift Sources
|
||||||
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
|
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
|
||||||
|
|
||||||
|
# combine bdk-ffi static libs for aarch64 and x86_64 targets via lipo tool
|
||||||
mkdir -p target/lipo-ios-sim/release-smaller
|
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
|
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
|
mkdir -p target/lipo-macos/release-smaller
|
||||||
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
||||||
|
|
||||||
cd ../bdk-swift/ || exit
|
cd ../bdk-swift/ || exit
|
||||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
|
# move bdk-ffi static lib header files to temporary directory
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
|
mkdir -p "${NEW_HEADER_DIR}"
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
mv "${HEADERPATH}" "${NEW_HEADER_DIR}"
|
||||||
cp ../bdk-ffi/target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
mv "${MODMAPPATH}" "${NEW_HEADER_DIR}/module.modulemap"
|
||||||
cp ../bdk-ffi/target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
|
|
||||||
cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
# remove old xcframework directory
|
||||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
rm -rf "${OUTDIR}/${NAME}.xcframework"
|
||||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
|
||||||
|
# create new xcframework directory from bdk-ffi static libs and headers
|
||||||
|
xcodebuild -create-xcframework \
|
||||||
|
-library "${TARGETDIR}/lipo-macos/${RELDIR}/${STATIC_LIB_NAME}" \
|
||||||
|
-headers "${NEW_HEADER_DIR}" \
|
||||||
|
-library "${TARGETDIR}/aarch64-apple-ios/${RELDIR}/${STATIC_LIB_NAME}" \
|
||||||
|
-headers "${NEW_HEADER_DIR}" \
|
||||||
|
-library "${TARGETDIR}/lipo-ios-sim/${RELDIR}/${STATIC_LIB_NAME}" \
|
||||||
|
-headers "${NEW_HEADER_DIR}" \
|
||||||
|
-output "${OUTDIR}/${NAME}.xcframework"
|
@ -2,7 +2,7 @@ default:
|
|||||||
just --list
|
just --list
|
||||||
|
|
||||||
build:
|
build:
|
||||||
bash ./build-local-swift.sh
|
bash ./build-xcframework.sh
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf ../bdk-ffi/target/
|
rm -rf ../bdk-ffi/target/
|
||||||
@ -11,4 +11,4 @@ test:
|
|||||||
swift test
|
swift test
|
||||||
|
|
||||||
test-offline:
|
test-offline:
|
||||||
swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
swift test --skip LiveElectrumClientTests --skip LiveMemoryWalletTests --skip LiveTransactionTests --skip LiveTxBuilderTests --skip LiveWalletTests
|
Loading…
x
Reference in New Issue
Block a user