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