Compare commits
No commits in common. "frost" and "fix/live-tests" have entirely different histories.
frost
...
fix/live-t
2
.github/workflows/live-tests.yaml
vendored
2
.github/workflows/live-tests.yaml
vendored
@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: "Build Swift package"
|
||||
working-directory: bdk-swift
|
||||
run: bash ./build-xcframework.sh
|
||||
run: bash ./build-local-swift.sh
|
||||
|
||||
- name: "Run live Swift tests"
|
||||
working-directory: bdk-swift
|
||||
|
19
.github/workflows/publish-python.yaml
vendored
19
.github/workflows/publish-python.yaml
vendored
@ -1,17 +1,22 @@
|
||||
name: Publish bdkpython to PyPI
|
||||
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:
|
||||
build-manylinux_2_28-x86_64-wheels:
|
||||
name: "Build Manylinux 2.28 x86_64 wheel"
|
||||
build-manylinux2014-x86_64-wheels:
|
||||
name: "Build Manylinux 2014 x86_64 wheel"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux_2_28_x86_64
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux_2_28_x86_64
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -38,11 +43,11 @@ jobs:
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_28_x86_64 --verbose
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-arm64-wheels:
|
||||
@ -163,7 +168,7 @@ jobs:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
needs: [build-manylinux_2_28-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
||||
needs: [build-manylinux2014-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
17
.github/workflows/test-python.yaml
vendored
17
.github/workflows/test-python.yaml
vendored
@ -10,17 +10,22 @@ on:
|
||||
- "bdk-ffi/**"
|
||||
- "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:
|
||||
build-manylinux_2_28-x86_64-wheels:
|
||||
name: "Build and test Manylinux 2.28 x86_64 wheels"
|
||||
build-manylinux2014-x86_64-wheels:
|
||||
name: "Build and test Manylinux 2014 x86_64 wheels"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux_2_28_x86_64
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux_2_28_x86_64
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -47,7 +52,7 @@ jobs:
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_28_x86_64 --verbose
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: ${PYBIN}/pip install ./dist/*.whl
|
||||
@ -58,7 +63,7 @@ jobs:
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
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"
|
||||
working-directory: bdk-swift
|
||||
run: bash ./build-xcframework.sh
|
||||
run: bash ./build-local-swift.sh
|
||||
|
||||
- name: "Run Swift tests"
|
||||
working-directory: bdk-swift
|
||||
run: swift test --skip LiveElectrumClientTests --skip LiveMemoryWalletTests --skip LiveTransactionTests --skip LiveTxBuilderTests --skip LiveWalletTests
|
||||
run: swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -31,8 +31,6 @@ bdkFFI.h
|
||||
BitcoinDevKit.swift
|
||||
bdk.swift
|
||||
.build
|
||||
*.xcframework/
|
||||
Info.plist
|
||||
|
||||
# Python related
|
||||
__pycache__
|
32
CHANGELOG.md
32
CHANGELOG.md
@ -3,27 +3,10 @@ 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).
|
||||
|
||||
## [v1.0.0-alpha.11]
|
||||
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.
|
||||
|
||||
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]
|
||||
## [1.0.0-alpha.7]
|
||||
This release brings back into the 1.0 API a number of APIs from the 0.31 release, and adds the new flat file persistence feature, as well as more fine-grain errors.
|
||||
|
||||
## [v1.0.0-alpha.2a]
|
||||
## [1.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:
|
||||
- Create and recover wallets using descriptors, including the four descriptor templates
|
||||
- Sync a wallet using a blocking Esplora client
|
||||
@ -31,7 +14,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
|
||||
- Broadcast transactions
|
||||
|
||||
## [v0.31.0]
|
||||
## [0.31.0]
|
||||
This release updates the bindings libraries to bdk version 0.29.0, updating rust-bitcoin to version 0.30.2.
|
||||
|
||||
- APIs Changed:
|
||||
@ -43,7 +26,7 @@ This release updates the bindings libraries to bdk version 0.29.0, updating rust
|
||||
|
||||
[#443]: https://github.com/bitcoindevkit/bdk-ffi/pull/443
|
||||
|
||||
## [v0.30.0]
|
||||
## [0.30.0]
|
||||
This release has a new API and a few internal optimizations and refactorings.
|
||||
|
||||
- APIs Added
|
||||
@ -51,7 +34,7 @@ This release has a new API and a few internal optimizations and refactorings.
|
||||
|
||||
[#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388
|
||||
|
||||
## [v0.29.0]
|
||||
## [0.29.0]
|
||||
This release has a number of new APIs, and adds support for Windows in bdk-jvm.
|
||||
|
||||
Changelog
|
||||
@ -262,9 +245,8 @@ Changelog
|
||||
|
||||
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
||||
|
||||
[v1.0.0-alpha.11]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.7...v1.0.0-alpha.11
|
||||
[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
|
||||
[1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.2a...v1.0.0-alpha.7
|
||||
[1.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.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
|
||||
|
@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.12-SNAPSHOT
|
||||
libraryVersion=1.0.0-alpha.10-SNAPSHOT
|
||||
|
@ -17,4 +17,4 @@ test:
|
||||
./gradlew connectedAndroidTest
|
||||
|
||||
test-specific TEST:
|
||||
./gradlew test --tests {{TEST}}
|
||||
./gradlew test --tests {{TEST}}
|
@ -18,7 +18,7 @@ android {
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
|
@ -13,9 +13,8 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveTxBuilderTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence3.sqlite"
|
||||
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence.db"
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
@ -27,20 +26,20 @@ class LiveTxBuilderTest {
|
||||
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
|
||||
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."
|
||||
}
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
@ -50,23 +49,23 @@ class LiveTxBuilderTest {
|
||||
|
||||
@Test
|
||||
fun complexTxBuilder() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.TESTNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
|
||||
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."
|
||||
}
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
|
||||
val allRecipients: List<ScriptAmount> = listOf(
|
||||
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient1.scriptPubkey(), 4200uL),
|
||||
ScriptAmount(recipient2.scriptPubkey(), 4200uL),
|
||||
)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
|
@ -14,9 +14,7 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveWalletTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_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)
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence.db"
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
@ -28,24 +26,24 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
val balance: Balance = wallet.balance()
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
val balance: Balance = wallet.getBalance()
|
||||
println("Balance: $balance")
|
||||
|
||||
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."
|
||||
}
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Transaction: ${tx.transaction.txid()}")
|
||||
println("Sent ${sentAndReceived.sent}")
|
||||
println("Received ${sentAndReceived.received}")
|
||||
}
|
||||
@ -53,21 +51,24 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
println("New address: ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
assert(wallet.getBalance().total > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.finish(wallet)
|
||||
|
||||
@ -78,10 +79,10 @@ class LiveWalletTest {
|
||||
assertTrue(walletDidSign)
|
||||
|
||||
val tx: Transaction = psbt.extractTx()
|
||||
println("Txid is: ${tx.computeTxid()}")
|
||||
println("Txid is: ${tx.txid()}")
|
||||
|
||||
val txFee: Amount = wallet.calculateFee(tx)
|
||||
println("Tx fee is: ${txFee.toSat()}")
|
||||
val txFee: ULong = wallet.calculateFee(tx)
|
||||
println("Tx fee is: ${txFee}")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||
|
@ -15,7 +15,7 @@ class OfflineDescriptorTest {
|
||||
|
||||
assertEquals(
|
||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
actual = descriptor.toString()
|
||||
actual = descriptor.asString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,7 @@ import kotlin.test.AfterTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineWalletTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.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)
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence.db"
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
@ -31,14 +29,19 @@ class OfflineWalletTest {
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertTrue(descriptor.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewAddress() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
null,
|
||||
persistenceFilePath,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
@ -49,22 +52,27 @@ class OfflineWalletTest {
|
||||
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||
|
||||
assertEquals(
|
||||
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
actual = addressInfo.address.toString()
|
||||
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
|
||||
actual = addressInfo.address.asString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
null,
|
||||
persistenceFilePath,
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.balance().total.toSat()
|
||||
actual = wallet.getBalance().total
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -34,10 +34,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android24-clang"),
|
||||
Pair("CC", "aarch64-linux-android24-clang")
|
||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
|
||||
Pair("CC", "aarch64-linux-android21-clang")
|
||||
)
|
||||
|
||||
doLast {
|
||||
@ -57,10 +57,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android24-clang"),
|
||||
Pair("CC", "x86_64-linux-android24-clang")
|
||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
|
||||
Pair("CC", "x86_64-linux-android21-clang")
|
||||
)
|
||||
|
||||
doLast {
|
||||
@ -80,10 +80,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi24-clang"),
|
||||
Pair("CC", "armv7a-linux-androideabi24-clang")
|
||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi21-clang"),
|
||||
Pair("CC", "armv7a-linux-androideabi21-clang")
|
||||
)
|
||||
|
||||
doLast {
|
||||
|
659
bdk-ffi/Cargo.lock
generated
659
bdk-ffi/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "1.0.0-alpha.11"
|
||||
version = "1.0.0-alpha.10"
|
||||
homepage = "https://bitcoindevkit.org"
|
||||
repository = "https://github.com/bitcoindevkit/bdk"
|
||||
edition = "2018"
|
||||
@ -18,24 +18,19 @@ path = "uniffi-bindgen.rs"
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.15.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
||||
# NOTE: This is a temporary workaround to use the electrum-client with the use-rustls-ring feature. It points to a fork
|
||||
# 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"] }
|
||||
# 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"] }
|
||||
bdk = { version = "1.0.0-alpha.10", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.12.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
||||
bdk_file_store = { version = "0.10.0" }
|
||||
|
||||
uniffi = { version = "=0.28.0" }
|
||||
uniffi = { version = "=0.26.1" }
|
||||
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
|
||||
thiserror = "1.0.58"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "=0.28.0", features = ["build"] }
|
||||
uniffi = { version = "=0.26.1", features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
uniffi = { version = "=0.28.0", features = ["bindgen-tests"] }
|
||||
uniffi = { version = "=0.26.1", features = ["bindgen-tests"] }
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[profile.release-smaller]
|
||||
|
@ -5,17 +5,16 @@ namespace bdk {};
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Error]
|
||||
interface AddressParseError {
|
||||
interface AddressError {
|
||||
Base58();
|
||||
Bech32();
|
||||
WitnessVersion(string error_message);
|
||||
WitnessProgram(string error_message);
|
||||
UnknownHrp();
|
||||
LegacyAddressTooLong();
|
||||
InvalidBase58PayloadLength();
|
||||
InvalidLegacyPrefix();
|
||||
NetworkValidation();
|
||||
OtherAddressParseErr();
|
||||
UncompressedPubkey();
|
||||
ExcessiveScriptSize();
|
||||
UnrecognizedScript();
|
||||
NetworkValidation(Network required, Network found, string address);
|
||||
OtherAddressErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
@ -45,7 +44,7 @@ interface Bip39Error {
|
||||
[Error]
|
||||
interface CalculateFeeError {
|
||||
MissingTxOut(sequence<OutPoint> out_points);
|
||||
NegativeFee(string amount);
|
||||
NegativeFee(i64 fee);
|
||||
};
|
||||
|
||||
[Error]
|
||||
@ -56,6 +55,7 @@ interface CannotConnectError {
|
||||
[Error]
|
||||
interface CreateTxError {
|
||||
Descriptor(string error_message);
|
||||
Persist(string error_message);
|
||||
Policy(string error_message);
|
||||
SpendingPolicyRequired(string kind);
|
||||
Version0();
|
||||
@ -63,7 +63,7 @@ interface CreateTxError {
|
||||
LockTime(string requested, string required);
|
||||
RbfSequence();
|
||||
RbfSequenceCsv(string rbf, string csv);
|
||||
FeeTooLow(string required);
|
||||
FeeTooLow(u64 required);
|
||||
FeeRateTooLow(string required);
|
||||
NoUtxosSelected();
|
||||
OutputBelowDustLimit(u64 index);
|
||||
@ -92,7 +92,6 @@ interface DescriptorError {
|
||||
Pk(string error_message);
|
||||
Miniscript(string error_message);
|
||||
Hex(string error_message);
|
||||
ExternalAndInternalAreTheSame();
|
||||
};
|
||||
|
||||
[Error]
|
||||
@ -102,27 +101,6 @@ interface DescriptorKeyError {
|
||||
Bip32(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ElectrumError {
|
||||
IOError(string error_message);
|
||||
Json(string error_message);
|
||||
Hex(string error_message);
|
||||
Protocol(string error_message);
|
||||
Bitcoin(string error_message);
|
||||
AlreadySubscribed();
|
||||
NotSubscribed();
|
||||
InvalidResponse(string error_message);
|
||||
Message(string error_message);
|
||||
InvalidDNSNameError(string domain);
|
||||
MissingDomain();
|
||||
AllAttemptsErrored();
|
||||
SharedIOError(string error_message);
|
||||
CouldntLockReader();
|
||||
Mpsc();
|
||||
CouldNotCreateConnection(string error_message);
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface EsploraError {
|
||||
Minreq(string error_message);
|
||||
@ -153,77 +131,17 @@ enum FeeRateError {
|
||||
"ArithmeticOverflow"
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface FromScriptError {
|
||||
UnrecognizedScript();
|
||||
WitnessProgram(string error_message);
|
||||
WitnessVersion(string error_message);
|
||||
OtherFromScriptErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ParseAmountError {
|
||||
OutOfRange();
|
||||
TooPrecise();
|
||||
MissingDigits();
|
||||
InputTooLarge();
|
||||
InvalidCharacter(string error_message);
|
||||
OtherParseAmountErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PersistenceError {
|
||||
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]
|
||||
interface PsbtParseError {
|
||||
PsbtEncoding(string error_message);
|
||||
Base64Encoding(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface InspectError {
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface SignerError {
|
||||
MissingKey();
|
||||
@ -237,19 +155,11 @@ interface SignerError {
|
||||
MissingHdKeypath();
|
||||
NonStandardSighash();
|
||||
InvalidSighash();
|
||||
SighashP2wpkh(string error_message);
|
||||
SighashTaproot(string error_message);
|
||||
TxInputsIndexError(string error_message);
|
||||
SighashError(string error_message);
|
||||
MiniscriptPsbt(string error_message);
|
||||
External(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface SqliteError {
|
||||
InvalidNetwork(Network expected, Network given);
|
||||
Sqlite(string rusqlite_error);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface TransactionError {
|
||||
Io();
|
||||
@ -268,14 +178,17 @@ interface TxidParseError {
|
||||
|
||||
[Error]
|
||||
interface WalletCreationError {
|
||||
Descriptor(string error_message);
|
||||
Io(string error_message);
|
||||
InvalidMagicBytes(sequence<u8> got, sequence<u8> expected);
|
||||
Descriptor();
|
||||
Persist(string error_message);
|
||||
NotInitialized();
|
||||
LoadedGenesisDoesNotMatch(string expected, string got);
|
||||
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
||||
LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_wallet crate - types module
|
||||
// bdk crate - types module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum KeychainKind {
|
||||
@ -290,17 +203,17 @@ dictionary AddressInfo {
|
||||
};
|
||||
|
||||
dictionary Balance {
|
||||
Amount immature;
|
||||
u64 immature;
|
||||
|
||||
Amount trusted_pending;
|
||||
u64 trusted_pending;
|
||||
|
||||
Amount untrusted_pending;
|
||||
u64 untrusted_pending;
|
||||
|
||||
Amount confirmed;
|
||||
u64 confirmed;
|
||||
|
||||
Amount trusted_spendable;
|
||||
u64 trusted_spendable;
|
||||
|
||||
Amount total;
|
||||
u64 total;
|
||||
};
|
||||
|
||||
dictionary LocalOutput {
|
||||
@ -326,30 +239,12 @@ dictionary CanonicalTx {
|
||||
ChainPosition chain_position;
|
||||
};
|
||||
|
||||
interface FullScanRequest {
|
||||
[Throws=InspectError]
|
||||
FullScanRequest inspect_spks_for_all_keychains(FullScanScriptInspector inspector);
|
||||
};
|
||||
interface FullScanRequest {};
|
||||
|
||||
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 {};
|
||||
interface SyncRequest {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_wallet crate - wallet module
|
||||
// bdk crate - wallet module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum ChangeSpendPolicy {
|
||||
@ -360,20 +255,21 @@ enum ChangeSpendPolicy {
|
||||
|
||||
interface Wallet {
|
||||
[Throws=WalletCreationError]
|
||||
constructor(Descriptor descriptor, Descriptor change_descriptor, Network network);
|
||||
|
||||
[Name=new_or_load, Throws=WalletCreationError]
|
||||
constructor(Descriptor descriptor, Descriptor change_descriptor, ChangeSet? change_set, Network network);
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, string persistence_backend_path, Network network);
|
||||
|
||||
[Throws=PersistenceError]
|
||||
AddressInfo reveal_next_address(KeychainKind keychain);
|
||||
|
||||
Network network();
|
||||
|
||||
Balance balance();
|
||||
Balance get_balance();
|
||||
|
||||
[Throws=CannotConnectError]
|
||||
void apply_update(Update update);
|
||||
|
||||
[Throws=PersistenceError]
|
||||
boolean commit();
|
||||
|
||||
boolean is_mine([ByRef] Script script);
|
||||
|
||||
[Throws=SignerError]
|
||||
@ -387,7 +283,7 @@ interface Wallet {
|
||||
CanonicalTx? get_tx(string txid);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
Amount calculate_fee([ByRef] Transaction tx);
|
||||
u64 calculate_fee([ByRef] Transaction tx);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
||||
@ -399,8 +295,6 @@ interface Wallet {
|
||||
FullScanRequest start_full_scan();
|
||||
|
||||
SyncRequest start_sync_with_revealed_spks();
|
||||
|
||||
ChangeSet? take_staged();
|
||||
};
|
||||
|
||||
interface Update {};
|
||||
@ -408,7 +302,7 @@ interface Update {};
|
||||
interface TxBuilder {
|
||||
constructor();
|
||||
|
||||
TxBuilder add_recipient([ByRef] Script script, Amount amount);
|
||||
TxBuilder add_recipient([ByRef] Script script, u64 amount);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||
|
||||
@ -428,7 +322,7 @@ interface TxBuilder {
|
||||
|
||||
TxBuilder fee_rate([ByRef] FeeRate fee_rate);
|
||||
|
||||
TxBuilder fee_absolute(Amount fee);
|
||||
TxBuilder fee_absolute(u64 fee);
|
||||
|
||||
TxBuilder drain_wallet();
|
||||
|
||||
@ -453,26 +347,10 @@ interface BumpFeeTxBuilder {
|
||||
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
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Traits=(Display)]
|
||||
interface Mnemonic {
|
||||
constructor(WordCount word_count);
|
||||
|
||||
@ -481,6 +359,8 @@ interface Mnemonic {
|
||||
|
||||
[Name=from_entropy, Throws=Bip39Error]
|
||||
constructor(sequence<u8> entropy);
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface DerivationPath {
|
||||
@ -520,7 +400,6 @@ interface DescriptorPublicKey {
|
||||
string as_string();
|
||||
};
|
||||
|
||||
[Traits=(Display)]
|
||||
interface Descriptor {
|
||||
[Throws=DescriptorError]
|
||||
constructor(string descriptor, Network network);
|
||||
@ -549,7 +428,9 @@ interface Descriptor {
|
||||
[Name=new_bip86_public]
|
||||
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
string to_string_with_secret();
|
||||
string as_string();
|
||||
|
||||
string as_string_private();
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -569,40 +450,22 @@ interface EsploraClient {
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_electrum crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface ElectrumClient {
|
||||
[Throws=ElectrumError]
|
||||
constructor(string url);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 batch_size, boolean fetch_prev_txouts);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
Update sync(SyncRequest sync_request, u64 batch_size, boolean fetch_prev_txouts);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
string broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk-ffi-defined types
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
Amount amount;
|
||||
u64 amount;
|
||||
};
|
||||
|
||||
dictionary SentAndReceivedValues {
|
||||
Amount sent;
|
||||
Amount received;
|
||||
u64 sent;
|
||||
u64 received;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_wallet crate - bitcoin re-exports
|
||||
// bdk crate - bitcoin re-exports
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface Script {
|
||||
@ -627,18 +490,18 @@ enum WordCount {
|
||||
"Words24",
|
||||
};
|
||||
|
||||
[Traits=(Display)]
|
||||
interface Address {
|
||||
[Throws=AddressParseError]
|
||||
[Throws=AddressError]
|
||||
constructor(string address, Network network);
|
||||
|
||||
[Name=from_script, Throws=FromScriptError]
|
||||
constructor(Script script, Network network);
|
||||
Network network();
|
||||
|
||||
Script script_pubkey();
|
||||
|
||||
string to_qr_uri();
|
||||
|
||||
string as_string();
|
||||
|
||||
boolean is_valid_for_network(Network network);
|
||||
};
|
||||
|
||||
@ -646,7 +509,7 @@ interface Transaction {
|
||||
[Throws=TransactionError]
|
||||
constructor(sequence<u8> transaction_bytes);
|
||||
|
||||
string compute_txid();
|
||||
string txid();
|
||||
|
||||
u64 total_size();
|
||||
|
||||
@ -663,12 +526,6 @@ interface Transaction {
|
||||
sequence<u8> serialize();
|
||||
|
||||
u64 weight();
|
||||
|
||||
sequence<TxIn> input();
|
||||
|
||||
sequence<TxOut> output();
|
||||
|
||||
u32 lock_time();
|
||||
};
|
||||
|
||||
interface Psbt {
|
||||
@ -679,14 +536,6 @@ interface Psbt {
|
||||
|
||||
[Throws=ExtractTxError]
|
||||
Transaction extract_tx();
|
||||
|
||||
[Throws=PsbtError]
|
||||
u64 fee();
|
||||
|
||||
[Throws=PsbtError]
|
||||
Psbt combine(Psbt other);
|
||||
|
||||
string json_serialize();
|
||||
};
|
||||
|
||||
dictionary OutPoint {
|
||||
@ -694,18 +543,6 @@ dictionary OutPoint {
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
interface Amount {
|
||||
[Name=from_sat]
|
||||
constructor(u64 from_sat);
|
||||
|
||||
[Name=from_btc, Throws=ParseAmountError]
|
||||
constructor(f64 from_btc);
|
||||
|
||||
u64 to_sat();
|
||||
|
||||
f64 to_btc();
|
||||
};
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb, Throws=FeeRateError]
|
||||
constructor(u64 sat_per_vb);
|
||||
@ -719,10 +556,3 @@ interface FeeRate {
|
||||
|
||||
u64 to_sat_per_kwu();
|
||||
};
|
||||
|
||||
dictionary TxIn {
|
||||
OutPoint previous_output;
|
||||
Script script_sig;
|
||||
u32 sequence;
|
||||
sequence<sequence<u8>> witness;
|
||||
};
|
||||
|
@ -1,65 +1,23 @@
|
||||
use crate::error::{
|
||||
AddressParseError, FeeRateError, FromScriptError, PsbtError, PsbtParseError, TransactionError,
|
||||
};
|
||||
use crate::error::{AddressError, FeeRateError, PsbtParseError, TransactionError};
|
||||
|
||||
use bdk_bitcoind_rpc::bitcoincore_rpc::jsonrpc::serde_json;
|
||||
use bdk_wallet::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk_wallet::bitcoin::amount::ParseAmountError;
|
||||
use bdk_wallet::bitcoin::consensus::encode::serialize;
|
||||
use bdk_wallet::bitcoin::consensus::Decodable;
|
||||
use bdk_wallet::bitcoin::io::Cursor;
|
||||
use bdk_wallet::bitcoin::psbt::ExtractTxError;
|
||||
use bdk_wallet::bitcoin::Address as BdkAddress;
|
||||
use bdk_wallet::bitcoin::Amount as BdkAmount;
|
||||
use bdk_wallet::bitcoin::FeeRate as BdkFeeRate;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk_wallet::bitcoin::TxIn as BdkTxIn;
|
||||
use bdk_wallet::bitcoin::TxOut as BdkTxOut;
|
||||
use bdk_wallet::bitcoin::Txid;
|
||||
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
|
||||
use bdk::bitcoin::consensus::encode::serialize;
|
||||
use bdk::bitcoin::consensus::Decodable;
|
||||
use bdk::bitcoin::psbt::ExtractTxError;
|
||||
use bdk::bitcoin::Address as BdkAddress;
|
||||
use bdk::bitcoin::FeeRate as BdkFeeRate;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::bitcoin::Txid;
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::ops::Deref;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Amount(pub(crate) BdkAmount);
|
||||
|
||||
impl Amount {
|
||||
pub fn from_sat(sat: u64) -> Self {
|
||||
Amount(BdkAmount::from_sat(sat))
|
||||
}
|
||||
|
||||
pub fn from_btc(btc: f64) -> Result<Self, ParseAmountError> {
|
||||
let bdk_amount = BdkAmount::from_btc(btc).map_err(ParseAmountError::from)?;
|
||||
Ok(Amount(bdk_amount))
|
||||
}
|
||||
|
||||
pub fn to_sat(&self) -> u64 {
|
||||
self.0.to_sat()
|
||||
}
|
||||
|
||||
pub fn to_btc(&self) -> f64 {
|
||||
self.0.to_btc()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Amount> for BdkAmount {
|
||||
fn from(amount: Amount) -> Self {
|
||||
amount.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAmount> for Amount {
|
||||
fn from(amount: BdkAmount) -> Self {
|
||||
Amount(amount)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Script(pub(crate) BdkScriptBuf);
|
||||
|
||||
@ -84,17 +42,15 @@ impl From<BdkScriptBuf> for Script {
|
||||
pub struct Address(BdkAddress<NetworkChecked>);
|
||||
|
||||
impl Address {
|
||||
pub fn new(address: String, network: Network) -> Result<Self, AddressParseError> {
|
||||
let parsed_address = address.parse::<bdk_wallet::bitcoin::Address<NetworkUnchecked>>()?;
|
||||
pub fn new(address: String, network: Network) -> Result<Self, AddressError> {
|
||||
let parsed_address = address.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()?;
|
||||
let network_checked_address = parsed_address.require_network(network)?;
|
||||
|
||||
Ok(Address(network_checked_address))
|
||||
}
|
||||
|
||||
pub fn from_script(script: Arc<Script>, network: Network) -> Result<Self, FromScriptError> {
|
||||
let address = BdkAddress::from_script(&script.0.clone(), network)?;
|
||||
|
||||
Ok(Address(address))
|
||||
pub fn network(&self) -> Network {
|
||||
*self.0.network()
|
||||
}
|
||||
|
||||
pub fn script_pubkey(&self) -> Arc<Script> {
|
||||
@ -105,6 +61,10 @@ impl Address {
|
||||
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 {
|
||||
let address_str = self.0.to_string();
|
||||
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
|
||||
@ -115,12 +75,6 @@ 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 {
|
||||
fn from(address: Address) -> Self {
|
||||
address.0
|
||||
@ -143,8 +97,8 @@ impl Transaction {
|
||||
Ok(Transaction(tx))
|
||||
}
|
||||
|
||||
pub fn compute_txid(&self) -> String {
|
||||
self.0.compute_txid().to_string()
|
||||
pub fn txid(&self) -> String {
|
||||
self.0.txid().to_string()
|
||||
}
|
||||
|
||||
pub fn weight(&self) -> u64 {
|
||||
@ -178,18 +132,6 @@ impl Transaction {
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
serialize(&self.0)
|
||||
}
|
||||
|
||||
pub fn input(&self) -> Vec<TxIn> {
|
||||
self.0.input.iter().map(|tx_in| tx_in.into()).collect()
|
||||
}
|
||||
|
||||
pub fn output(&self) -> Vec<TxOut> {
|
||||
self.0.output.iter().map(|tx_out| tx_out.into()).collect()
|
||||
}
|
||||
|
||||
pub fn lock_time(&self) -> u32 {
|
||||
self.0.lock_time.to_consensus_u32()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkTransaction> for Transaction {
|
||||
@ -228,27 +170,6 @@ impl Psbt {
|
||||
let transaction: Transaction = tx.into();
|
||||
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 {
|
||||
@ -281,28 +202,6 @@ impl From<&BdkOutPoint> for OutPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxIn {
|
||||
pub previous_output: OutPoint,
|
||||
pub script_sig: Arc<Script>,
|
||||
pub sequence: u32,
|
||||
pub witness: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl From<&BdkTxIn> for TxIn {
|
||||
fn from(tx_in: &BdkTxIn) -> Self {
|
||||
TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: tx_in.previous_output.txid.to_string(),
|
||||
vout: tx_in.previous_output.vout,
|
||||
},
|
||||
script_sig: Arc::new(Script(tx_in.script_sig.clone())),
|
||||
sequence: tx_in.sequence.0,
|
||||
witness: tx_in.witness.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxOut {
|
||||
pub value: u64,
|
||||
@ -319,7 +218,7 @@ impl From<&BdkTxOut> for TxOut {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FeeRate(pub(crate) BdkFeeRate);
|
||||
pub struct FeeRate(pub BdkFeeRate);
|
||||
|
||||
impl FeeRate {
|
||||
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
||||
@ -372,6 +271,11 @@ mod tests {
|
||||
docs_address_testnet.is_valid_for_network(Network::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 =
|
||||
@ -380,6 +284,21 @@ mod tests {
|
||||
docs_address_mainnet.is_valid_for_network(Network::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====
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
use crate::error::DescriptorError;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use std::fmt::Display;
|
||||
|
||||
use bdk_wallet::bitcoin::bip32::Fingerprint;
|
||||
use bdk_wallet::bitcoin::key::Secp256k1;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||
use bdk_wallet::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||
use bdk_wallet::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||
use bdk_wallet::template::{
|
||||
use bdk::bitcoin::bip32::Fingerprint;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||
use bdk::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||
use bdk::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||
use bdk::template::{
|
||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
||||
DescriptorTemplate,
|
||||
};
|
||||
use bdk_wallet::KeychainKind;
|
||||
use bdk::KeychainKind;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -261,16 +260,14 @@ impl Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_string_with_secret(&self) -> String {
|
||||
pub(crate) fn as_string_private(&self) -> String {
|
||||
let descriptor = &self.extended_descriptor;
|
||||
let key_map = &self.key_map;
|
||||
descriptor.to_string_with_secret(key_map)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Descriptor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.extended_descriptor)
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.extended_descriptor.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,8 +275,6 @@ impl Display for Descriptor {
|
||||
mod test {
|
||||
use crate::*;
|
||||
use assert_matches::assert_matches;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
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();
|
||||
@ -324,10 +319,10 @@ mod test {
|
||||
let template_private_86 =
|
||||
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
|
||||
println!("Template 49: {}", template_private_49);
|
||||
println!("Template 44: {}", template_private_44);
|
||||
println!("Template 84: {}", template_private_84);
|
||||
println!("Template 86: {}", template_private_86);
|
||||
println!("Template 49: {}", template_private_49.as_string());
|
||||
println!("Template 44: {}", template_private_44.as_string());
|
||||
println!("Template 84: {}", template_private_84.as_string());
|
||||
println!("Template 86: {}", template_private_86.as_string());
|
||||
let template_public_44 = Descriptor::new_bip44_public(
|
||||
&handmade_public_44,
|
||||
"d1d04177".to_string(),
|
||||
@ -352,43 +347,43 @@ mod test {
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
println!("Template public 49: {}", template_public_49);
|
||||
println!("Template public 44: {}", template_public_44);
|
||||
println!("Template public 84: {}", template_public_84);
|
||||
println!("Template public 86: {}", template_public_86);
|
||||
// when using a public key, both to_string and as_string_private return the same string
|
||||
println!("Template public 49: {}", template_public_49.as_string());
|
||||
println!("Template public 44: {}", template_public_44.as_string());
|
||||
println!("Template public 84: {}", template_public_84.as_string());
|
||||
println!("Template public 86: {}", template_public_86.as_string());
|
||||
// when using a public key, both as_string and as_string_private return the same string
|
||||
assert_eq!(
|
||||
template_public_44.to_string_with_secret(),
|
||||
template_public_44.to_string()
|
||||
template_public_44.as_string_private(),
|
||||
template_public_44.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_49.to_string_with_secret(),
|
||||
template_public_49.to_string()
|
||||
template_public_49.as_string_private(),
|
||||
template_public_49.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_84.to_string_with_secret(),
|
||||
template_public_84.to_string()
|
||||
template_public_84.as_string_private(),
|
||||
template_public_84.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_86.to_string_with_secret(),
|
||||
template_public_86.to_string()
|
||||
template_public_86.as_string_private(),
|
||||
template_public_86.as_string()
|
||||
);
|
||||
// when using to_string on a private key, we get the same result as when using it on a public key
|
||||
// when using as_string on a private key, we get the same result as when using it on a public key
|
||||
assert_eq!(
|
||||
template_private_44.to_string(),
|
||||
template_public_44.to_string()
|
||||
template_private_44.as_string(),
|
||||
template_public_44.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_49.to_string(),
|
||||
template_public_49.to_string()
|
||||
template_private_49.as_string(),
|
||||
template_public_49.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_84.to_string(),
|
||||
template_public_84.to_string()
|
||||
template_private_84.as_string(),
|
||||
template_public_84.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_86.to_string(),
|
||||
template_public_86.to_string()
|
||||
template_private_86.as_string(),
|
||||
template_public_86.as_string()
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -1,101 +0,0 @@
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::error::ElectrumError;
|
||||
use crate::types::{FullScanRequest, SyncRequest};
|
||||
use crate::wallet::Update;
|
||||
|
||||
use bdk_electrum::BdkElectrumClient as BdkBdkElectrumClient;
|
||||
use bdk_electrum::{ElectrumFullScanResult, ElectrumSyncResult};
|
||||
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;
|
||||
|
||||
// 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 {
|
||||
pub fn new(url: String) -> Result<Self, ElectrumError> {
|
||||
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))
|
||||
}
|
||||
|
||||
pub fn full_scan(
|
||||
&self,
|
||||
request: Arc<FullScanRequest>,
|
||||
stop_gap: u64,
|
||||
batch_size: u64,
|
||||
fetch_prev_txouts: bool,
|
||||
) -> Result<Arc<Update>, ElectrumError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkFullScanRequest<KeychainKind> = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||
|
||||
let electrum_result: ElectrumFullScanResult<KeychainKind> = self.0.full_scan(
|
||||
request,
|
||||
stop_gap as usize,
|
||||
batch_size as usize,
|
||||
fetch_prev_txouts,
|
||||
)?;
|
||||
let full_scan_result: BdkFullScanResult<KeychainKind> =
|
||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices: full_scan_result.last_active_indices,
|
||||
graph: full_scan_result.graph_update,
|
||||
chain: Some(full_scan_result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn sync(
|
||||
&self,
|
||||
request: Arc<SyncRequest>,
|
||||
batch_size: u64,
|
||||
fetch_prev_txouts: bool,
|
||||
) -> Result<Arc<Update>, ElectrumError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkSyncRequest = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||
|
||||
let electrum_result: ElectrumSyncResult =
|
||||
self.0
|
||||
.sync(request, batch_size as usize, fetch_prev_txouts)?;
|
||||
let sync_result: BdkSyncResult =
|
||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices: BTreeMap::default(),
|
||||
graph: sync_result.graph_update,
|
||||
chain: Some(sync_result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<String, ElectrumError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
self.0
|
||||
.transaction_broadcast(&bdk_transaction)
|
||||
.map_err(ElectrumError::from)
|
||||
.map(|txid| txid.to_string())
|
||||
}
|
||||
}
|
1264
bdk-ffi/src/error.rs
1264
bdk-ffi/src/error.rs
File diff suppressed because it is too large
Load Diff
@ -2,18 +2,17 @@ use crate::bitcoin::Transaction;
|
||||
use crate::error::EsploraError;
|
||||
use crate::types::{FullScanRequest, SyncRequest};
|
||||
use crate::wallet::Update;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk::chain::spk_client::SyncResult as BdkSyncResult;
|
||||
use bdk::KeychainKind;
|
||||
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||
use bdk_esplora::EsploraExt;
|
||||
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;
|
||||
|
||||
pub struct EsploraClient(BlockingClient);
|
||||
@ -42,7 +41,7 @@ impl EsploraClient {
|
||||
self.0
|
||||
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
let update = bdk::wallet::Update {
|
||||
last_active_indices: result.last_active_indices,
|
||||
graph: result.graph_update,
|
||||
chain: Some(result.chain_update),
|
||||
@ -66,7 +65,7 @@ impl EsploraClient {
|
||||
|
||||
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
let update = bdk::wallet::Update {
|
||||
last_active_indices: BTreeMap::default(),
|
||||
graph: result.graph_update,
|
||||
chain: Some(result.chain_update),
|
||||
|
@ -1,25 +1,24 @@
|
||||
use crate::error::{Bip32Error, Bip39Error, DescriptorKeyError};
|
||||
use std::fmt::Display;
|
||||
|
||||
use bdk_wallet::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk_wallet::bitcoin::key::Secp256k1;
|
||||
use bdk_wallet::bitcoin::secp256k1::rand;
|
||||
use bdk_wallet::bitcoin::secp256k1::rand::Rng;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::keys::bip39::WordCount;
|
||||
use bdk_wallet::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||
use bdk_wallet::keys::{
|
||||
use bdk::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
use bdk::bitcoin::secp256k1::rand;
|
||||
use bdk::bitcoin::secp256k1::rand::Rng;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||
use bdk::keys::{
|
||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||
};
|
||||
use bdk_wallet::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
||||
use bdk_wallet::miniscript::BareCtx;
|
||||
use bdk::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
||||
use bdk::miniscript::BareCtx;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub(crate) struct Mnemonic(BdkMnemonic);
|
||||
pub(crate) struct Mnemonic(pub(crate) BdkMnemonic);
|
||||
|
||||
impl Mnemonic {
|
||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||
@ -45,11 +44,9 @@ impl Mnemonic {
|
||||
.map(Mnemonic)
|
||||
.map_err(Bip39Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Mnemonic {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,9 +224,10 @@ impl DescriptorPublicKey {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::error::DescriptorKeyError;
|
||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
// use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use crate::error::DescriptorKeyError;
|
||||
use bdk::bitcoin::Network;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn get_inner() -> DescriptorSecretKey {
|
||||
|
@ -1,25 +1,20 @@
|
||||
mod bitcoin;
|
||||
mod descriptor;
|
||||
mod electrum;
|
||||
mod error;
|
||||
mod esplora;
|
||||
mod keys;
|
||||
mod store;
|
||||
mod types;
|
||||
mod wallet;
|
||||
|
||||
use crate::bitcoin::Address;
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::FeeRate;
|
||||
use crate::bitcoin::OutPoint;
|
||||
use crate::bitcoin::Psbt;
|
||||
use crate::bitcoin::Script;
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::bitcoin::TxIn;
|
||||
use crate::bitcoin::TxOut;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::electrum::ElectrumClient;
|
||||
use crate::error::AddressParseError;
|
||||
use crate::error::AddressError;
|
||||
use crate::error::Bip32Error;
|
||||
use crate::error::Bip39Error;
|
||||
use crate::error::CalculateFeeError;
|
||||
@ -27,18 +22,12 @@ use crate::error::CannotConnectError;
|
||||
use crate::error::CreateTxError;
|
||||
use crate::error::DescriptorError;
|
||||
use crate::error::DescriptorKeyError;
|
||||
use crate::error::ElectrumError;
|
||||
use crate::error::EsploraError;
|
||||
use crate::error::ExtractTxError;
|
||||
use crate::error::FeeRateError;
|
||||
use crate::error::FromScriptError;
|
||||
use crate::error::InspectError;
|
||||
use crate::error::ParseAmountError;
|
||||
use crate::error::PersistenceError;
|
||||
use crate::error::PsbtError;
|
||||
use crate::error::PsbtParseError;
|
||||
use crate::error::SignerError;
|
||||
use crate::error::SqliteError;
|
||||
use crate::error::TransactionError;
|
||||
use crate::error::TxidParseError;
|
||||
use crate::error::WalletCreationError;
|
||||
@ -47,27 +36,23 @@ use crate::keys::DerivationPath;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use crate::keys::Mnemonic;
|
||||
use crate::store::SqliteStore;
|
||||
use crate::types::AddressInfo;
|
||||
use crate::types::Balance;
|
||||
use crate::types::CanonicalTx;
|
||||
use crate::types::ChainPosition;
|
||||
use crate::types::ChangeSet;
|
||||
use crate::types::FullScanRequest;
|
||||
use crate::types::FullScanScriptInspector;
|
||||
use crate::types::LocalOutput;
|
||||
use crate::types::ScriptAmount;
|
||||
use crate::types::SyncRequest;
|
||||
use crate::types::SyncScriptInspector;
|
||||
use crate::wallet::BumpFeeTxBuilder;
|
||||
use crate::wallet::SentAndReceivedValues;
|
||||
use crate::wallet::TxBuilder;
|
||||
use crate::wallet::Update;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::keys::bip39::WordCount;
|
||||
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk_wallet::KeychainKind;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::KeychainKind;
|
||||
|
||||
uniffi::include_scaffolding!("bdk");
|
||||
|
@ -1,39 +0,0 @@
|
||||
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,19 +1,14 @@
|
||||
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::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 bdk_electrum::bdk_chain::CombinedChangeSet;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -27,8 +22,12 @@ pub struct CanonicalTx {
|
||||
pub chain_position: ChainPosition,
|
||||
}
|
||||
|
||||
impl From<BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>> for CanonicalTx {
|
||||
fn from(tx: BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>) -> Self {
|
||||
impl From<BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHeightAnchor>>
|
||||
for CanonicalTx
|
||||
{
|
||||
fn from(
|
||||
tx: BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHeightAnchor>,
|
||||
) -> Self {
|
||||
let chain_position = match tx.chain_position {
|
||||
BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed {
|
||||
height: anchor.confirmation_height,
|
||||
@ -46,7 +45,7 @@ impl From<BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>>
|
||||
|
||||
pub struct ScriptAmount {
|
||||
pub script: Arc<Script>,
|
||||
pub amount: Arc<Amount>,
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
pub struct AddressInfo {
|
||||
@ -66,35 +65,27 @@ impl From<BdkAddressInfo> for AddressInfo {
|
||||
}
|
||||
|
||||
pub struct Balance {
|
||||
pub immature: Arc<Amount>,
|
||||
pub trusted_pending: Arc<Amount>,
|
||||
pub untrusted_pending: Arc<Amount>,
|
||||
pub confirmed: Arc<Amount>,
|
||||
pub trusted_spendable: Arc<Amount>,
|
||||
pub total: Arc<Amount>,
|
||||
pub immature: u64,
|
||||
pub trusted_pending: u64,
|
||||
pub untrusted_pending: u64,
|
||||
pub confirmed: u64,
|
||||
pub trusted_spendable: u64,
|
||||
pub total: u64,
|
||||
}
|
||||
|
||||
impl From<BdkBalance> for Balance {
|
||||
fn from(bdk_balance: BdkBalance) -> Self {
|
||||
Balance {
|
||||
immature: Arc::new(bdk_balance.immature.into()),
|
||||
trusted_pending: Arc::new(bdk_balance.trusted_pending.into()),
|
||||
untrusted_pending: Arc::new(bdk_balance.untrusted_pending.into()),
|
||||
confirmed: Arc::new(bdk_balance.confirmed.into()),
|
||||
trusted_spendable: Arc::new(bdk_balance.trusted_spendable().into()),
|
||||
total: Arc::new(bdk_balance.total().into()),
|
||||
immature: bdk_balance.immature,
|
||||
trusted_pending: bdk_balance.trusted_pending,
|
||||
untrusted_pending: bdk_balance.untrusted_pending,
|
||||
confirmed: bdk_balance.confirmed,
|
||||
trusted_spendable: bdk_balance.trusted_spendable(),
|
||||
total: bdk_balance.total(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 outpoint: OutPoint,
|
||||
pub txout: TxOut,
|
||||
@ -119,55 +110,6 @@ 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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,29 @@
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::error::{
|
||||
CalculateFeeError, CannotConnectError, CreateTxError, SignerError, TxidParseError,
|
||||
WalletCreationError,
|
||||
CalculateFeeError, CannotConnectError, CreateTxError, PersistenceError, SignerError,
|
||||
TxidParseError, WalletCreationError,
|
||||
};
|
||||
use crate::types::{
|
||||
AddressInfo, Balance, CanonicalTx, ChangeSet, FullScanRequest, LocalOutput, ScriptAmount,
|
||||
SyncRequest,
|
||||
AddressInfo, Balance, CanonicalTx, FullScanRequest, LocalOutput, ScriptAmount, SyncRequest,
|
||||
};
|
||||
|
||||
use bdk_wallet::bitcoin::amount::Amount as BdkAmount;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||
use bdk_wallet::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk_wallet::chain::{CombinedChangeSet, ConfirmationTimeHeightAnchor};
|
||||
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||
use bdk_wallet::Wallet as BdkWallet;
|
||||
use bdk_wallet::{KeychainKind, SignOptions};
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::wallet::{ChangeSet, Update as BdkUpdate};
|
||||
use bdk::Wallet as BdkWallet;
|
||||
use bdk::{KeychainKind, SignOptions};
|
||||
use bdk_file_store::Store;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
const MAGIC_BYTES: &[u8] = "bdkffi".as_bytes();
|
||||
|
||||
pub struct Wallet {
|
||||
inner_mutex: Mutex<BdkWallet>,
|
||||
}
|
||||
@ -32,30 +31,16 @@ pub struct Wallet {
|
||||
impl Wallet {
|
||||
pub fn new(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Option<Arc<Descriptor>>,
|
||||
persistence_backend_path: String,
|
||||
network: Network,
|
||||
) -> Result<Self, WalletCreationError> {
|
||||
let descriptor = descriptor.to_string_with_secret();
|
||||
let change_descriptor = change_descriptor.to_string_with_secret();
|
||||
let wallet: BdkWallet = BdkWallet::new(&descriptor, &change_descriptor, network)?;
|
||||
let descriptor = descriptor.as_string_private();
|
||||
let change_descriptor = change_descriptor.map(|d| d.as_string_private());
|
||||
let db = Store::<ChangeSet>::open_or_create_new(MAGIC_BYTES, persistence_backend_path)?;
|
||||
|
||||
Ok(Wallet {
|
||||
inner_mutex: Mutex::new(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_or_load(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Arc<Descriptor>,
|
||||
change_set: Option<Arc<ChangeSet>>,
|
||||
network: Network,
|
||||
) -> Result<Self, WalletCreationError> {
|
||||
let descriptor = descriptor.to_string_with_secret();
|
||||
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 =
|
||||
BdkWallet::new_or_load(&descriptor, &change_descriptor, change_set, network)?;
|
||||
BdkWallet::new_or_load(&descriptor, change_descriptor.as_ref(), db, network)?;
|
||||
|
||||
Ok(Wallet {
|
||||
inner_mutex: Mutex::new(wallet),
|
||||
@ -66,8 +51,16 @@ impl Wallet {
|
||||
self.inner_mutex.lock().expect("wallet")
|
||||
}
|
||||
|
||||
pub fn reveal_next_address(&self, keychain_kind: KeychainKind) -> AddressInfo {
|
||||
self.get_wallet().reveal_next_address(keychain_kind).into()
|
||||
pub fn reveal_next_address(
|
||||
&self,
|
||||
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> {
|
||||
@ -76,12 +69,20 @@ impl Wallet {
|
||||
.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 {
|
||||
self.get_wallet().network()
|
||||
}
|
||||
|
||||
pub fn balance(&self) -> Balance {
|
||||
let bdk_balance = self.get_wallet().balance();
|
||||
pub fn get_balance(&self) -> Balance {
|
||||
let bdk_balance: bdk::wallet::Balance = self.get_wallet().get_balance();
|
||||
Balance::from(bdk_balance)
|
||||
}
|
||||
|
||||
@ -101,11 +102,8 @@ impl Wallet {
|
||||
}
|
||||
|
||||
pub fn sent_and_received(&self, tx: &Transaction) -> SentAndReceivedValues {
|
||||
let (sent, received) = self.get_wallet().sent_and_received(&tx.into());
|
||||
SentAndReceivedValues {
|
||||
sent: Arc::new(sent.into()),
|
||||
received: Arc::new(received.into()),
|
||||
}
|
||||
let (sent, received): (u64, u64) = self.get_wallet().sent_and_received(&tx.into());
|
||||
SentAndReceivedValues { sent, received }
|
||||
}
|
||||
|
||||
pub fn transactions(&self) -> Vec<CanonicalTx> {
|
||||
@ -121,11 +119,9 @@ impl Wallet {
|
||||
Ok(self.get_wallet().get_tx(txid).map(|tx| tx.into()))
|
||||
}
|
||||
|
||||
pub fn calculate_fee(&self, tx: &Transaction) -> Result<Arc<Amount>, CalculateFeeError> {
|
||||
pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
|
||||
self.get_wallet()
|
||||
.calculate_fee(&tx.into())
|
||||
.map(Amount::from)
|
||||
.map(Arc::new)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
@ -153,30 +149,24 @@ impl Wallet {
|
||||
let request = self.get_wallet().start_sync_with_revealed_spks();
|
||||
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 sent: Arc<Amount>,
|
||||
pub received: Arc<Amount>,
|
||||
pub sent: u64,
|
||||
pub received: u64,
|
||||
}
|
||||
|
||||
pub struct Update(pub(crate) BdkUpdate);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TxBuilder {
|
||||
pub(crate) recipients: Vec<(BdkScriptBuf, BdkAmount)>,
|
||||
pub(crate) recipients: Vec<(BdkScriptBuf, u64)>,
|
||||
pub(crate) utxos: Vec<OutPoint>,
|
||||
pub(crate) unspendable: HashSet<OutPoint>,
|
||||
pub(crate) change_policy: ChangeSpendPolicy,
|
||||
pub(crate) manually_selected_only: bool,
|
||||
pub(crate) fee_rate: Option<FeeRate>,
|
||||
pub(crate) fee_absolute: Option<Arc<Amount>>,
|
||||
pub(crate) fee_absolute: Option<u64>,
|
||||
pub(crate) drain_wallet: bool,
|
||||
pub(crate) drain_to: Option<BdkScriptBuf>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
@ -200,9 +190,9 @@ impl TxBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_recipient(&self, script: &Script, amount: Arc<Amount>) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScriptBuf, BdkAmount)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.0.clone(), amount.0)]);
|
||||
pub(crate) fn add_recipient(&self, script: &Script, amount: u64) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScriptBuf, u64)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.0.clone(), amount)]);
|
||||
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
@ -213,7 +203,7 @@ impl TxBuilder {
|
||||
pub(crate) fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
|
||||
let recipients = recipients
|
||||
.iter()
|
||||
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount.0)) //;
|
||||
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount))
|
||||
.collect();
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
@ -285,7 +275,7 @@ impl TxBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn fee_absolute(&self, fee_amount: Arc<Amount>) -> Arc<Self> {
|
||||
pub(crate) fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
fee_absolute: Some(fee_amount),
|
||||
..self.clone()
|
||||
@ -345,8 +335,8 @@ impl TxBuilder {
|
||||
if let Some(fee_rate) = &self.fee_rate {
|
||||
tx_builder.fee_rate(fee_rate.0);
|
||||
}
|
||||
if let Some(fee_amount) = &self.fee_absolute {
|
||||
tx_builder.fee_absolute(fee_amount.0);
|
||||
if let Some(fee_amount) = self.fee_absolute {
|
||||
tx_builder.fee_absolute(fee_amount);
|
||||
}
|
||||
if self.drain_wallet {
|
||||
tx_builder.drain_wallet();
|
||||
|
@ -4,6 +4,6 @@
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import BitcoinDevKit
|
||||
import bdk
|
||||
|
||||
let network = Network.testnet
|
||||
|
@ -5,6 +5,8 @@ cdylib_name = "bdkffi"
|
||||
[bindings.python]
|
||||
cdylib_name = "bdkffi"
|
||||
|
||||
[bindings.swift]
|
||||
module_name = "BitcoinDevKit"
|
||||
[bindings.ruby]
|
||||
cdylib_name = "bdkffi"
|
||||
|
||||
[bindings.swift]
|
||||
cdylib_name = "bdkffi"
|
||||
|
@ -1,4 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.12-SNAPSHOT
|
||||
libraryVersion=1.0.0-alpha.10-SNAPSHOT
|
||||
|
@ -24,16 +24,13 @@ java {
|
||||
|
||||
// This block ensures that the tests that require access to a blockchain are not
|
||||
// run if the -P excludeConnectedTests flag is passed to gradle.
|
||||
// This ensures our CI runs are not fickle by not requiring access to testnet or signet.
|
||||
// This ensures our CI runs are not fickle by not requiring access to testnet.
|
||||
// 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
|
||||
tasks.test {
|
||||
if (project.hasProperty("excludeConnectedTests")) {
|
||||
exclude("**/LiveElectrumClientTest.class")
|
||||
exclude("**/LiveMemoryWalletTest.class")
|
||||
exclude("**/LiveTransactionTest.class")
|
||||
exclude("**/LiveTxBuilderTest.class")
|
||||
exclude("**/LiveWalletTest.class")
|
||||
exclude("**/LiveTxBuilderTest.class")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||
|
||||
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
|
||||
fun testSyncedBalance() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val electrumClient: ElectrumClient = ElectrumClient(SIGNET_ELECTRUM_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = electrumClient.fullScan(fullScanRequest, 10uL, 10uL, false)
|
||||
wallet.applyUpdate(update)
|
||||
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."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||
println("Received ${sentAndReceived.received.toSat()}")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveMemoryWalletTest {
|
||||
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 testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.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."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Sent ${sentAndReceived.sent.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")
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveTransactionTests {
|
||||
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 testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Wallet 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."
|
||||
}
|
||||
|
||||
val transaction: Transaction = wallet.transactions().first().transaction
|
||||
println("First transaction:")
|
||||
println("Txid: ${transaction.computeTxid()}")
|
||||
println("Version: ${transaction.version()}")
|
||||
println("Total size: ${transaction.totalSize()}")
|
||||
println("Vsize: ${transaction.vsize()}")
|
||||
println("Weight: ${transaction.weight()}")
|
||||
println("Coinbase transaction: ${transaction.isCoinbase()}")
|
||||
println("Is explicitly RBF: ${transaction.isExplicitlyRbf()}")
|
||||
println("Inputs: ${transaction.input()}")
|
||||
println("Outputs: ${transaction.output()}")
|
||||
}
|
||||
}
|
@ -11,16 +11,8 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
class LiveTxBuilderTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
"$currentDirectory/bdk_persistence.sqlite"
|
||||
"$currentDirectory/bdk_persistence.db"
|
||||
}
|
||||
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
|
||||
fun cleanup() {
|
||||
@ -33,20 +25,19 @@ class LiveTxBuilderTest {
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
|
||||
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."
|
||||
}
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
@ -57,22 +48,23 @@ class LiveTxBuilderTest {
|
||||
|
||||
@Test
|
||||
fun complexTxBuilder() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val externalDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
|
||||
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
|
||||
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."
|
||||
}
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
|
||||
val allRecipients: List<ScriptAmount> = listOf(
|
||||
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient1.scriptPubkey(), 4200uL),
|
||||
ScriptAmount(recipient2.scriptPubkey(), 4200uL),
|
||||
)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
|
@ -11,16 +11,8 @@ private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
class LiveWalletTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
"$currentDirectory/bdk_persistence.sqlite"
|
||||
"$currentDirectory/bdk_persistence.db"
|
||||
}
|
||||
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
|
||||
fun cleanup() {
|
||||
@ -32,22 +24,22 @@ class LiveWalletTest {
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
|
||||
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."
|
||||
}
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Transaction: ${tx.transaction.txid()}")
|
||||
println("Sent ${sentAndReceived.sent}")
|
||||
println("Received ${sentAndReceived.received}")
|
||||
}
|
||||
@ -56,21 +48,23 @@ class LiveWalletTest {
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val 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(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
wallet.commit()
|
||||
println("Balance: ${wallet.getBalance().total}")
|
||||
println("New address: ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()}")
|
||||
|
||||
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."
|
||||
assert(wallet.getBalance().total > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
@ -81,9 +75,9 @@ class LiveWalletTest {
|
||||
assertTrue(walletDidSign)
|
||||
|
||||
val tx: Transaction = psbt.extractTx()
|
||||
println("Txid is: ${tx.computeTxid()}")
|
||||
println("Txid is: ${tx.txid()}")
|
||||
|
||||
val txFee: Amount = wallet.calculateFee(tx)
|
||||
val txFee: ULong = wallet.calculateFee(tx)
|
||||
println("Tx fee is: ${txFee}")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
|
@ -12,7 +12,7 @@ class OfflineDescriptorTest {
|
||||
|
||||
assertEquals(
|
||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
actual = descriptor.toString()
|
||||
actual = descriptor.asString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
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,16 +10,8 @@ import kotlin.test.assertFalse
|
||||
class OfflineWalletTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
"$currentDirectory/bdk_persistence.sqlite"
|
||||
"$currentDirectory/bdk_persistence.db"
|
||||
}
|
||||
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
|
||||
fun cleanup() {
|
||||
@ -35,14 +27,19 @@ class OfflineWalletTest {
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertTrue(descriptor.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewAddress() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
null,
|
||||
persistenceFilePath,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
@ -53,22 +50,27 @@ class OfflineWalletTest {
|
||||
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||
|
||||
assertEquals(
|
||||
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
actual = addressInfo.address.toString()
|
||||
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
|
||||
actual = addressInfo.address.asString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
null,
|
||||
persistenceFilePath,
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.balance().total.toSat()
|
||||
actual = wallet.getBalance().total
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -18,7 +18,7 @@ import bdkpython as bdk
|
||||
|
||||
setup(
|
||||
name="bdkpython",
|
||||
version="1.0.0a12.dev",
|
||||
version="1.0.0a10.dev",
|
||||
description="The Python language bindings for the Bitcoin Development Kit",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type="text/markdown",
|
||||
|
@ -5,29 +5,21 @@ import os
|
||||
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
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):
|
||||
|
||||
def tearDown(self) -> None:
|
||||
if os.path.exists("./bdk_persistence.sqlite"):
|
||||
os.remove("./bdk_persistence.sqlite")
|
||||
if os.path.exists("./bdk_persistence.db"):
|
||||
os.remove("./bdk_persistence.db")
|
||||
|
||||
def test_tx_builder(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
@ -38,26 +30,32 @@ class LiveTxBuilderTest(unittest.TestCase):
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(
|
||||
wallet.balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||
)
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
|
||||
recipient = bdk.Address(
|
||||
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network=bdk.Network.SIGNET
|
||||
)
|
||||
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=bdk.Amount.from_sat(4200)).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
def complex_tx_builder(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
change_descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
@ -68,12 +66,9 @@ class LiveTxBuilderTest(unittest.TestCase):
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(
|
||||
wallet.balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||
)
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
|
||||
recipient1 = bdk.Address(
|
||||
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
|
@ -5,25 +5,21 @@ import os
|
||||
SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
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):
|
||||
|
||||
def tearDown(self) -> None:
|
||||
if os.path.exists("./bdk_persistence.sqlite"):
|
||||
os.remove("./bdk_persistence.sqlite")
|
||||
if os.path.exists("./bdk_persistence.db"):
|
||||
os.remove("./bdk_persistence.db")
|
||||
|
||||
def test_synced_balance(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
@ -34,26 +30,28 @@ class LiveWalletTest(unittest.TestCase):
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(
|
||||
wallet.balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||
)
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
|
||||
print(f"Transactions count: {len(wallet.transactions())}")
|
||||
transactions = wallet.transactions()[:3]
|
||||
for tx in transactions:
|
||||
sent_and_received = wallet.sent_and_received(tx.transaction)
|
||||
print(f"Transaction: {tx.transaction.compute_txid()}")
|
||||
print(f"Sent {sent_and_received.sent.to_sat()}")
|
||||
print(f"Received {sent_and_received.received.to_sat()}")
|
||||
print(f"Transaction: {tx.transaction.txid()}")
|
||||
print(f"Sent {sent_and_received.sent}")
|
||||
print(f"Received {sent_and_received.received}")
|
||||
|
||||
|
||||
def test_broadcast_transaction(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.SIGNET
|
||||
)
|
||||
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = SIGNET_ESPLORA_URL)
|
||||
@ -64,27 +62,25 @@ class LiveWalletTest(unittest.TestCase):
|
||||
parallel_requests=1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
wallet.commit()
|
||||
|
||||
self.assertGreater(
|
||||
wallet.balance().total.to_sat(),
|
||||
0,
|
||||
f"Wallet balance must be greater than 0! Please send funds to {wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL).address} and try again."
|
||||
)
|
||||
self.assertGreater(wallet.get_balance().total, 0)
|
||||
|
||||
recipient = bdk.Address(
|
||||
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network=bdk.Network.SIGNET
|
||||
)
|
||||
|
||||
psbt: bdk.Psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=bdk.Amount.from_sat(4200)).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
psbt: bdk.Psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
# print(psbt.serialize())
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
walletDidSign = wallet.sign(psbt)
|
||||
self.assertTrue(walletDidSign)
|
||||
tx = psbt.extract_tx()
|
||||
print(f"Transaction Id: {tx.compute_txid()}")
|
||||
print(f"Transaction Id: {tx.txid()}")
|
||||
fee = wallet.calculate_fee(tx)
|
||||
print(f"Transaction Fee: {fee.to_sat()}")
|
||||
print(f"Transaction Fee: {fee}")
|
||||
fee_rate = wallet.calculate_fee_rate(tx)
|
||||
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB")
|
||||
|
||||
|
@ -10,7 +10,7 @@ class OfflineDescriptorTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
"tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
descriptor.__str__()
|
||||
descriptor.as_string()
|
||||
)
|
||||
|
||||
|
||||
|
@ -2,25 +2,21 @@ import bdkpython as bdk
|
||||
import unittest
|
||||
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):
|
||||
|
||||
def tearDown(self) -> None:
|
||||
if os.path.exists("./bdk_persistence.sqlite"):
|
||||
os.remove("./bdk_persistence.sqlite")
|
||||
if os.path.exists("./bdk_persistence.db"):
|
||||
os.remove("./bdk_persistence.db")
|
||||
|
||||
def test_new_address(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
address_info: bdk.AddressInfo = wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL)
|
||||
@ -30,16 +26,21 @@ 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.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||
|
||||
self.assertEqual("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", address_info.address.__str__())
|
||||
self.assertEqual("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address_info.address.as_string())
|
||||
|
||||
def test_balance(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet(
|
||||
descriptor,
|
||||
change_descriptor,
|
||||
None,
|
||||
"./bdk_persistence.db",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
|
||||
self.assertEqual(wallet.balance().total.to_sat(), 0)
|
||||
self.assertEqual(wallet.get_balance().total, 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -33,10 +33,6 @@ let package = Package(
|
||||
),
|
||||
.testTarget(
|
||||
name: "BitcoinDevKitTests",
|
||||
dependencies: ["BitcoinDevKit"],
|
||||
resources: [
|
||||
.copy("Resources/pre_existing_wallet_persistence_test.sqlite")
|
||||
]
|
||||
),
|
||||
dependencies: ["BitcoinDevKit"]),
|
||||
]
|
||||
)
|
||||
|
@ -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.
|
||||
|
||||
The `bdk-swift/build-xcframework.sh` script can be used instead to create a version of the project for local testing.
|
||||
The `bdk-swift/build-local-swift.sh` script can be used instead to create a version of the project for local testing.
|
||||
|
||||
### How to test
|
||||
|
||||
|
@ -1,48 +0,0 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||
|
||||
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 {
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
network: Network.signet
|
||||
)
|
||||
let electrumClient: ElectrumClient = try ElectrumClient(url: SIGNET_ELECTRUM_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try electrumClient.fullScan(
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
batchSize: 10,
|
||||
fetchPrevTxouts: false
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.balance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.computeTxid())")
|
||||
print("Sent \(sentAndReceived.sent.toSat())")
|
||||
print("Received \(sentAndReceived.received.toSat())")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
final class LiveMemoryWalletTests: XCTestCase {
|
||||
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 {
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let 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)"
|
||||
)
|
||||
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.computeTxid())")
|
||||
print("Sent \(sentAndReceived.sent.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)")
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
|
||||
final class LiveTransactionTests: XCTestCase {
|
||||
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 {
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
let update = try esploraClient.fullScan(
|
||||
fullScanRequest: fullScanRequest,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let 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)"
|
||||
)
|
||||
|
||||
guard let transaction = wallet.transactions().first?.transaction else {
|
||||
print("No transactions found")
|
||||
return
|
||||
}
|
||||
print("First transaction:")
|
||||
print("Txid: \(transaction.computeTxid())")
|
||||
print("Version: \(transaction.version())")
|
||||
print("Total size: \(transaction.totalSize())")
|
||||
print("Vsize: \(transaction.vsize())")
|
||||
print("Weight: \(transaction.weight())")
|
||||
print("Coinbase transaction: \(transaction.isCoinbase())")
|
||||
print("Is explicitly RBF: \(transaction.isExplicitlyRbf())")
|
||||
print("Inputs: \(transaction.input())")
|
||||
print("Outputs: \(transaction.output())")
|
||||
|
||||
}
|
||||
}
|
@ -1,25 +1,17 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
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!
|
||||
|
||||
override func setUpWithError() throws {
|
||||
super.setUp()
|
||||
let fileManager = FileManager.default
|
||||
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).sqlite"
|
||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).db"
|
||||
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
||||
|
||||
if fileManager.fileExists(atPath: dbFilePath.path) {
|
||||
@ -35,9 +27,14 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testTxBuilder() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
@ -48,17 +45,13 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||
try wallet.commit()
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.balance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||
let psbt: Psbt = try TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200))
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2))
|
||||
.finish(wallet: wallet)
|
||||
|
||||
@ -78,6 +71,7 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
@ -88,19 +82,15 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
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)"
|
||||
)
|
||||
try wallet.commit()
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
|
||||
let recipient1: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||
let recipient2: Address = try Address(address: "tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", network: .signet)
|
||||
let allRecipients: [ScriptAmount] = [
|
||||
ScriptAmount(script: recipient1.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200)),
|
||||
ScriptAmount(script: recipient2.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200))
|
||||
ScriptAmount(script: recipient1.scriptPubkey(), amount: 4200),
|
||||
ScriptAmount(script: recipient2.scriptPubkey(), amount: 4200)
|
||||
]
|
||||
|
||||
let psbt: Psbt = try TxBuilder()
|
||||
@ -110,7 +100,7 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
.enableRbf()
|
||||
.finish(wallet: wallet)
|
||||
|
||||
let _ = try! wallet.sign(psbt: psbt)
|
||||
try! wallet.sign(psbt: psbt)
|
||||
|
||||
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")
|
||||
}
|
||||
|
@ -1,25 +1,17 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
private let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
let SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
let TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
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!
|
||||
|
||||
override func setUpWithError() throws {
|
||||
super.setUp()
|
||||
let fileManager = FileManager.default
|
||||
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).sqlite"
|
||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).db"
|
||||
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
||||
|
||||
if fileManager.fileExists(atPath: dbFilePath.path) {
|
||||
@ -35,9 +27,14 @@ final class LiveWalletTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSyncedBalance() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
@ -48,28 +45,29 @@ final class LiveWalletTests: XCTestCase {
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
let address = wallet.revealNextAddress(keychain: KeychainKind.external).address.description
|
||||
try wallet.commit()
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
wallet.balance().total.toSat(),
|
||||
UInt64(0),
|
||||
"Wallet must have positive balance, please send funds to \(address)"
|
||||
)
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0))
|
||||
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.computeTxid())")
|
||||
print("Sent \(sentAndReceived.sent.toSat())")
|
||||
print("Received \(sentAndReceived.received.toSat())")
|
||||
print("Transaction: \(tx.transaction.txid())")
|
||||
print("Sent \(sentAndReceived.sent)")
|
||||
print("Received \(sentAndReceived.received)")
|
||||
}
|
||||
}
|
||||
|
||||
func testBroadcastTransaction() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.signet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .signet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: SIGNET_ESPLORA_URL)
|
||||
@ -80,20 +78,16 @@ final class LiveWalletTests: XCTestCase {
|
||||
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)"
|
||||
)
|
||||
try wallet.commit()
|
||||
|
||||
print("Balance: \(wallet.balance().total)")
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
|
||||
print("Balance: \(wallet.getBalance().total)")
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .signet)
|
||||
let psbt: Psbt = try
|
||||
TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: Amount.fromSat(fromSat: 4200))
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2))
|
||||
.finish(wallet: wallet)
|
||||
|
||||
@ -104,8 +98,8 @@ final class LiveWalletTests: XCTestCase {
|
||||
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
|
||||
|
||||
let tx: Transaction = try! psbt.extractTx()
|
||||
print(tx.computeTxid())
|
||||
let fee: Amount = try wallet.calculateFee(tx: tx)
|
||||
print(tx.txid())
|
||||
let fee: UInt64 = try wallet.calculateFee(tx: tx)
|
||||
print("Transaction Fee: \(fee)")
|
||||
let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx)
|
||||
print("Transaction Fee Rate: \(feeRate.toSatPerVbCeil()) sat/vB")
|
||||
|
@ -15,6 +15,6 @@ final class OfflineDescriptorTests: XCTestCase {
|
||||
network: Network.testnet
|
||||
)
|
||||
|
||||
XCTAssertEqual(descriptor.description, "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx")
|
||||
XCTAssertEqual(descriptor.asString(), "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx")
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +0,0 @@
|
||||
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,21 +2,13 @@ import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
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!
|
||||
|
||||
override func setUpWithError() throws {
|
||||
super.setUp()
|
||||
let fileManager = FileManager.default
|
||||
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).sqlite"
|
||||
let uniqueDbFileName = "bdk_persistence_\(UUID().uuidString).db"
|
||||
dbFilePath = documentDirectory.appendingPathComponent(uniqueDbFileName)
|
||||
|
||||
if fileManager.fileExists(atPath: dbFilePath.path) {
|
||||
@ -32,12 +24,17 @@ final class OfflineWalletTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testNewAddress() throws {
|
||||
let descriptor: Descriptor = try Descriptor(
|
||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
)
|
||||
let addressInfo: AddressInfo = wallet.revealNextAddress(keychain: KeychainKind.external)
|
||||
let addressInfo: AddressInfo = try wallet.revealNextAddress(keychain: KeychainKind.external)
|
||||
|
||||
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.testnet),
|
||||
"Address is not valid for testnet network")
|
||||
@ -48,16 +45,21 @@ final class OfflineWalletTests: XCTestCase {
|
||||
XCTAssertFalse(addressInfo.address.isValidForNetwork(network: Network.bitcoin),
|
||||
"Address is valid for bitcoin network, but it shouldn't be")
|
||||
|
||||
XCTAssertEqual(addressInfo.address.description, "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989")
|
||||
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
|
||||
}
|
||||
|
||||
func testBalance() throws {
|
||||
let descriptor: Descriptor = try Descriptor(
|
||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet = try Wallet(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: changeDescriptor,
|
||||
changeDescriptor: nil,
|
||||
persistenceBackendPath: dbFilePath.path,
|
||||
network: .testnet
|
||||
)
|
||||
|
||||
XCTAssertEqual(wallet.balance().total.toSat(), 0)
|
||||
XCTAssertEqual(wallet.getBalance().total, 0)
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
59
bdk-swift/bdkFFI.xcframework/Info.plist
Normal file
59
bdk-swift/bdkFFI.xcframework/Info.plist
Normal file
@ -0,0 +1,59 @@
|
||||
<?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>
|
@ -0,0 +1,4 @@
|
||||
// This is the "umbrella header" for our combined Rust code library.
|
||||
// It needs to import all of the individual headers.
|
||||
|
||||
#import "bdkFFI.h"
|
@ -0,0 +1,18 @@
|
||||
<?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>
|
@ -0,0 +1,6 @@
|
||||
framework module bdkFFI {
|
||||
umbrella header "bdkFFI-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
// This is the "umbrella header" for our combined Rust code library.
|
||||
// It needs to import all of the individual headers.
|
||||
|
||||
#import "bdkFFI.h"
|
@ -0,0 +1,18 @@
|
||||
<?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>
|
@ -0,0 +1,6 @@
|
||||
framework module bdkFFI {
|
||||
umbrella header "bdkFFI-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
// This is the "umbrella header" for our combined Rust code library.
|
||||
// It needs to import all of the individual headers.
|
||||
|
||||
#import "bdkFFI.h"
|
@ -0,0 +1,18 @@
|
||||
<?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>
|
@ -0,0 +1,6 @@
|
||||
framework module bdkFFI {
|
||||
umbrella header "bdkFFI-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
@ -3,16 +3,6 @@
|
||||
# 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.
|
||||
|
||||
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 component add rust-src
|
||||
rustup target add aarch64-apple-ios # iOS arm64
|
||||
@ -23,38 +13,26 @@ rustup target add x86_64-apple-darwin # mac x86_64
|
||||
|
||||
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 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 aarch64-apple-ios
|
||||
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
|
||||
|
||||
# combine bdk-ffi static libs for aarch64 and x86_64 targets via lipo tool
|
||||
mkdir -p target/lipo-ios-sim/release-smaller
|
||||
lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-apple-ios/release-smaller/libbdkffi.a -create -output target/lipo-ios-sim/release-smaller/libbdkffi.a
|
||||
mkdir -p target/lipo-macos/release-smaller
|
||||
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
|
||||
|
||||
# move bdk-ffi static lib header files to temporary directory
|
||||
mkdir -p "${NEW_HEADER_DIR}"
|
||||
mv "${HEADERPATH}" "${NEW_HEADER_DIR}"
|
||||
mv "${MODMAPPATH}" "${NEW_HEADER_DIR}/module.modulemap"
|
||||
|
||||
# remove old xcframework directory
|
||||
rm -rf "${OUTDIR}/${NAME}.xcframework"
|
||||
|
||||
# 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"
|
||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||
cp ../bdk-ffi/target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
@ -2,7 +2,7 @@ default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
bash ./build-xcframework.sh
|
||||
bash ./build-local-swift.sh
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
@ -11,4 +11,4 @@ test:
|
||||
swift test
|
||||
|
||||
test-offline:
|
||||
swift test --skip LiveElectrumClientTests --skip LiveMemoryWalletTests --skip LiveTransactionTests --skip LiveTxBuilderTests --skip LiveWalletTests
|
||||
swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
Loading…
x
Reference in New Issue
Block a user