From 9433c637e4ffd3c75c3a009107c7f638888548d3 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Wed, 3 Jul 2024 15:12:12 -0400 Subject: [PATCH] feat: add compact block filter client temp 2 temp 3 temp 4 pass wallet to builder remove async builder make ip a type, bump bdk-kyoto, add connections add more configurations fix datadir builder bump kyoto add trait bound for logger update logger update bdk-ffi --- .gitignore | 2 + bdk-android/lib/build.gradle.kts | 2 + .../kotlin/org/bitcoindevkit/KyotoTest.kt | 24 ++ bdk-ffi/Cargo.lock | 253 +++++++++++++++++- bdk-ffi/Cargo.toml | 2 + bdk-ffi/src/bdk.udl | 98 ++++++- bdk-ffi/src/bitcoin.rs | 2 +- bdk-ffi/src/error.rs | 28 ++ bdk-ffi/src/kyoto.rs | 156 +++++++++++ bdk-ffi/src/lib.rs | 14 + bdk-jvm/justfile | 4 +- bdk-jvm/lib/build.gradle.kts | 2 + .../kotlin/org/bitcoindevkit/KyotoTest.kt | 19 ++ bdk-python/scripts/generate-linux.sh | 15 +- 14 files changed, 600 insertions(+), 21 deletions(-) create mode 100644 bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/KyotoTest.kt create mode 100644 bdk-ffi/src/kyoto.rs create mode 100644 bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/KyotoTest.kt diff --git a/.gitignore b/.gitignore index ada663fe..444005ec 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ local.properties *.log *.dylib *.so +*.db +*/data/signet .DS_Store testdb xcuserdata diff --git a/bdk-android/lib/build.gradle.kts b/bdk-android/lib/build.gradle.kts index 96d960ae..65c7743b 100644 --- a/bdk-android/lib/build.gradle.kts +++ b/bdk-android/lib/build.gradle.kts @@ -56,11 +56,13 @@ java { dependencies { implementation("net.java.dev.jna:jna:5.14.0@aar") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") implementation("androidx.appcompat:appcompat:1.4.0") implementation("androidx.core:core-ktx:1.7.0") api("org.slf4j:slf4j-api:1.7.30") androidTestImplementation("com.github.tony19:logback-android:2.0.0") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") androidTestImplementation("androidx.test.ext:junit:1.1.3") androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") androidTestImplementation("org.jetbrains.kotlin:kotlin-test:1.6.10") diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/KyotoTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/KyotoTest.kt new file mode 100644 index 00000000..27379d00 --- /dev/null +++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/KyotoTest.kt @@ -0,0 +1,24 @@ +package org.bitcoindevkit + +import org.junit.Test +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.runBlocking +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class KyotoTest { + private val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET) + private val changeDescriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET) + + @Test + fun testKyoto() { + // val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET) + runBlocking { + println("Running test Kyoto") + val (node, client) = buildKyotoClient() + runNode(node) + client.update() + println("Done running test Kyoto") + } + } +} diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index 91693661..b8ac2fd7 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.4.8" @@ -56,7 +71,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -66,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -134,6 +149,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base58ck" version = "0.1.0" @@ -180,9 +210,11 @@ dependencies = [ "bdk_core", "bdk_electrum", "bdk_esplora", + "bdk_kyoto", "bdk_wallet", "bitcoin-ffi", "thiserror", + "tokio", "uniffi", ] @@ -243,6 +275,16 @@ dependencies = [ "miniscript", ] +[[package]] +name = "bdk_kyoto" +version = "0.1.0" +source = "git+https://github.com/valuedmammal/bdk-kyoto?rev=58c6ee90206d9527ae78b664533aba0d8f8d2cb4#58c6ee90206d9527ae78b664533aba0d8f8d2cb4" +dependencies = [ + "bdk_chain", + "bdk_wallet", + "kyoto-cbf", +] + [[package]] name = "bdk_wallet" version = "1.0.0-beta.4" @@ -273,6 +315,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bip324" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2205cbdbe6a8ed43ea3b37ba6670a45d6617aceefcc85388672115bdf1efc91" +dependencies = [ + "bitcoin", + "rand", +] + [[package]] name = "bip39" version = "2.0.0" @@ -549,6 +601,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "glob" version = "0.3.1" @@ -600,6 +658,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hex-conservative" version = "0.2.1" @@ -639,6 +703,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kyoto-cbf" +version = "0.1.0" +source = "git+https://github.com/rustaceanrob/kyoto?rev=d9ab1ce3fdfee863f152fe145596b7f93171b5aa#d9ab1ce3fdfee863f152fe145596b7f93171b5aa" +dependencies = [ + "bip324", + "bitcoin", + "bitcoin_hashes 0.14.0", + "rusqlite", + "tokio", +] + [[package]] name = "libc" version = "0.2.155" @@ -701,6 +777,15 @@ dependencies = [ "serde", ] +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + [[package]] name = "minreq" version = "2.11.2" @@ -717,6 +802,17 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "nom" version = "7.1.3" @@ -727,6 +823,25 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -739,6 +854,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pkg-config" version = "0.3.30" @@ -817,7 +938,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -834,6 +955,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustls" version = "0.21.12" @@ -1003,6 +1130,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -1082,6 +1219,34 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.5.11" @@ -1313,13 +1478,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1328,28 +1517,46 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -1362,24 +1569,48 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index 72b7a276..d921d98a 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -22,10 +22,12 @@ bdk_wallet = { version = "1.0.0-beta.4", features = ["all-keys", "keys-bip39", " bdk_core = { version = "0.2.0" } bdk_esplora = { version = "0.18.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } bdk_electrum = { version = "0.18.0", default-features = false, features = ["use-rustls-ring"] } +bdk_kyoto = { git = "https://github.com/valuedmammal/bdk-kyoto", rev = "58c6ee90206d9527ae78b664533aba0d8f8d2cb4", default-features = false, features = ["wallet"] } bdk_bitcoind_rpc = { version = "0.15.0" } bitcoin-ffi = { git = "https://github.com/bitcoindevkit/bitcoin-ffi", tag = "v0.1.2" } uniffi = { version = "=0.28.0" } +tokio = { version = "1", default-features = false, features = [ "rt-multi-thread", "sync" ] } thiserror = "1.0.58" [build-dependencies] diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 2c8876e4..6f85a388 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -1,4 +1,9 @@ -namespace bdk {}; +namespace bdk { + [Throws=LightClientBuilderError] + NodePair build_light_client([ByRef] Wallet wallet, sequence peers, u8 connections, u32? recovery_height, boolean use_lookahead_scripts, string data_dir); + + void run_node(LightNode node); +}; // ------------------------------------------------------------------------ // bdk crate - error module @@ -265,6 +270,17 @@ interface TxidParseError { InvalidTxid(string txid); }; +[Error] +interface LightClientBuilderError { + DatabaseError(string reason); + ParseIpAddrError(); +}; + +[Error] +enum LightClientError { + "NodeStopped", +}; + // ------------------------------------------------------------------------ // bdk_wallet crate - types module // ------------------------------------------------------------------------ @@ -614,6 +630,86 @@ dictionary SentAndReceivedValues { Amount received; }; +// ------------------------------------------------------------------------ +// bdk-kyoto crate +// ------------------------------------------------------------------------ + +interface LightClient { + [Async] + Update? update(NodeMessageHandler? logger); + + [Async, Throws=LightClientError] + void broadcast(Transaction transaction); + + [Async, Throws=LightClientError] + void watch_address(Address address); + + [Async, Throws=LightClientError] + void shutdown(); + +}; + +interface LightNode { + [Async] + void run(); +}; + +dictionary NodePair { + LightNode node; + LightClient client; +}; + +[Enum] +interface Peer { + V4(u8 q1, u8 q2, u8 q3, u8 q4); + V6(string addr); + SocketAddr(string socket_addr); +}; + +[Trait, WithForeign] +interface NodeMessageHandler { + void dialog(string dialog); + + void warning(Warning warning); + + void state_changed(NodeState state); + + void connections_met(); + + void tx_sent(Txid txid); + + void tx_failed(Txid txid); + + void blocks_disconnected(sequence blocks); + + void synced(u32 tip); +}; + +[Enum] +interface Warning { + NotEnoughConnections(); + PeerTimedOut(); + CouldNotConnect(); + NoCompactFilters(); + PotentialStaleTip(); + UnsolicitedMessage(); + UnlinkableAnchor(); + CorruptedHeaders(); + TransactionRejected(); + FailedPersistance(string warning); + EvaluatingFork(); + EmptyPeerDatabase(); + UnexpectedSyncError(string warning); +}; + +enum NodeState { + "Behind", + "HeadersSynced", + "FilterHeadersSynced", + "FiltersSynced", + "TransactionsSynced" +}; + // ------------------------------------------------------------------------ // bdk_wallet crate - bitcoin re-exports // ------------------------------------------------------------------------ diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 29439fe3..3780f191 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -77,7 +77,7 @@ impl From for Address { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Transaction(BdkTransaction); +pub struct Transaction(pub(crate) BdkTransaction); impl Transaction { pub fn new(transaction_bytes: Vec) -> Result { diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index af9e3f0c..aa5e991a 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -622,6 +622,20 @@ pub enum TxidParseError { InvalidTxid { txid: String }, } +#[derive(Debug, thiserror::Error)] +pub enum LightClientBuilderError { + #[error("the database could not be opened or created: {reason}")] + DatabaseError { reason: String }, + #[error("the ip address provided could not be parsed correctly")] + ParseIpAddrError, +} + +#[derive(Debug, thiserror::Error)] +pub enum LightClientError { + #[error("the node is no longer running")] + NodeStopped, +} + // ------------------------------------------------------------------------ // error conversions // ------------------------------------------------------------------------ @@ -1210,6 +1224,20 @@ impl From for SqliteError { } } +impl From for LightClientBuilderError { + fn from(value: bdk_kyoto::builder::BuilderError) -> Self { + LightClientBuilderError::DatabaseError { + reason: value.to_string(), + } + } +} + +impl From for LightClientError { + fn from(_value: bdk_kyoto::ClientError) -> Self { + LightClientError::NodeStopped + } +} + // ------------------------------------------------------------------------ // error tests // ------------------------------------------------------------------------ diff --git a/bdk-ffi/src/kyoto.rs b/bdk-ffi/src/kyoto.rs new file mode 100644 index 00000000..11849580 --- /dev/null +++ b/bdk-ffi/src/kyoto.rs @@ -0,0 +1,156 @@ +use bdk_kyoto::builder::LightClientBuilder; +use bdk_kyoto::logger::{NodeMessageHandler, PrintLogger}; +use bdk_kyoto::Node; +use bdk_kyoto::{Client, TrustedPeer}; +use bdk_wallet::bitcoin::Transaction as BdkTransaction; +use bdk_wallet::KeychainKind; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::Mutex; + +use crate::bitcoin::{Address, Transaction}; +use crate::error::{LightClientBuilderError, LightClientError}; +use crate::wallet::Wallet; +use crate::Update; + +const TIMEOUT: u64 = 10; + +pub struct LightClient { + client: Mutex>, +} + +pub struct LightNode { + node: Mutex, +} + +pub struct NodePair { + pub node: Arc, + pub client: Arc, +} + +pub fn build_light_client( + wallet: &Wallet, + peers: Vec, + connections: u8, + recovery_height: Option, + use_lookahead_scripts: bool, + data_dir: String, +) -> Result { + let mut trusted_peers = Vec::new(); + for peer in peers { + match peer { + Peer::V4 { q1, q2, q3, q4 } => { + let ip = IpAddr::V4(Ipv4Addr::new(q1, q2, q3, q4)); + trusted_peers.push(TrustedPeer::from_ip(ip)); + } + Peer::V6 { addr } => { + let ip = IpAddr::V6( + Ipv6Addr::from_str(&addr) + .map_err(|_| LightClientBuilderError::ParseIpAddrError)?, + ); + trusted_peers.push(TrustedPeer::from_ip(ip)); + } + Peer::SocketAddr { socket_addr } => { + let socket_addr = SocketAddr::from_str(&socket_addr) + .map_err(|_| LightClientBuilderError::ParseIpAddrError)?; + trusted_peers.push(TrustedPeer::from_socket_addr(socket_addr)); + } + } + } + + let wallet = wallet.get_wallet(); + + let mut builder = LightClientBuilder::new(&wallet) + .connections(connections) + .data_dir(PathBuf::from_str(&data_dir).map_err(|e| { + LightClientBuilderError::DatabaseError { + reason: e.to_string(), + } + })?) + .timeout_duration(Duration::from_secs(TIMEOUT)) + .peers(trusted_peers); + + if let Some(recovery) = recovery_height { + builder = builder.scan_after(recovery) + } + + if use_lookahead_scripts { + builder = builder.use_lookahead_scripts() + } + + let (node, bdk_kyoto_client) = builder.build()?; + + let node = LightNode { + node: Mutex::new(node), + }; + + let client = LightClient { + client: Mutex::new(bdk_kyoto_client), + }; + + Ok(NodePair { + node: Arc::new(node), + client: Arc::new(client), + }) +} + +pub fn run_node(node: Arc) { + std::thread::spawn(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async move { + node.as_ref().run().await; + }) + }); +} + +impl LightClient { + pub async fn update(&self, logger: Option>) -> Option> { + let logger = logger.unwrap_or(Arc::new(PrintLogger::new())); + let update = self.client.lock().await.update(logger.as_ref()).await; + update.map(|update| Arc::new(Update(update.into()))) + } + + pub async fn broadcast(&self, transaction: Arc) -> Result<(), LightClientError> { + let client = self.client.lock().await; + let tx: BdkTransaction = match Arc::try_unwrap(transaction) { + Ok(val) => val.0, + Err(arc) => arc.0.clone(), + }; + client + .broadcast(tx, bdk_kyoto::TxBroadcastPolicy::RandomPeer) + .await + .map_err(From::from) + } + + pub async fn watch_address(&self, address: Arc
) -> Result<(), LightClientError> { + let client = self.client.lock().await; + let script = match Arc::try_unwrap(address.script_pubkey()) { + Ok(script) => script.into(), + Err(arc) => arc.0.clone(), + }; + client.add_script(script).await.map_err(From::from) + } + + pub async fn shutdown(&self) -> Result<(), LightClientError> { + let client = self.client.lock().await; + client.shutdown().await.map_err(From::from) + } +} + +impl LightNode { + pub async fn run(&self) { + let _ = self.node.lock().await.run().await; + } +} + +pub enum Peer { + V4 { q1: u8, q2: u8, q3: u8, q4: u8 }, + V6 { addr: String }, + SocketAddr { socket_addr: String }, +} diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index 110b21cc..560dcbc1 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -4,6 +4,7 @@ mod electrum; mod error; mod esplora; mod keys; +mod kyoto; mod store; mod tx_builder; mod types; @@ -29,6 +30,8 @@ use crate::error::ElectrumError; use crate::error::EsploraError; use crate::error::ExtractTxError; use crate::error::FromScriptError; +use crate::error::LightClientBuilderError; +use crate::error::LightClientError; use crate::error::LoadWithPersistError; use crate::error::PersistenceError; use crate::error::PsbtError; @@ -69,10 +72,21 @@ use bitcoin_ffi::FeeRate; use bitcoin_ffi::Network; use bitcoin_ffi::OutPoint; use bitcoin_ffi::Script; +use bitcoin_ffi::Txid; use bdk_wallet::keys::bip39::WordCount; use bdk_wallet::tx_builder::ChangeSpendPolicy; use bdk_wallet::ChangeSet; use bdk_wallet::KeychainKind; +use bdk_kyoto::logger::NodeMessageHandler; +use bdk_kyoto::NodeState; +use bdk_kyoto::Warning; +use kyoto::build_light_client; +use kyoto::run_node; +use kyoto::LightClient; +use kyoto::LightNode; +use kyoto::NodePair; +use kyoto::Peer; + uniffi::include_scaffolding!("bdk"); diff --git a/bdk-jvm/justfile b/bdk-jvm/justfile index 0e350898..e6174007 100644 --- a/bdk-jvm/justfile +++ b/bdk-jvm/justfile @@ -1,8 +1,8 @@ default: just --list -build: - ./gradlew buildJvmLib +build-macos: + bash ./scripts/build-macos-aarch64.sh clean: rm -rf ../bdk-ffi/target/ diff --git a/bdk-jvm/lib/build.gradle.kts b/bdk-jvm/lib/build.gradle.kts index 8c226d79..5c51aeab 100644 --- a/bdk-jvm/lib/build.gradle.kts +++ b/bdk-jvm/lib/build.gradle.kts @@ -58,12 +58,14 @@ tasks.withType { dependencies { implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") implementation("net.java.dev.jna:jna:5.14.0") api("org.slf4j:slf4j-api:1.7.30") // testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1") // testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.1") // testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") testImplementation("ch.qos.logback:logback-classic:1.2.3") testImplementation("ch.qos.logback:logback-core:1.2.3") } diff --git a/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/KyotoTest.kt b/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/KyotoTest.kt new file mode 100644 index 00000000..782ddc59 --- /dev/null +++ b/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/KyotoTest.kt @@ -0,0 +1,19 @@ +package org.bitcoindevkit + +import kotlinx.coroutines.runBlocking +import kotlin.test.Test + +class KyotoTest { + private val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET) + private val changeDescriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET) + + @Test + fun testKyoto() { + // val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET) + runBlocking { + val (node, client) = buildKyotoClient() + runNode(node) + client.update() + } + } +} diff --git a/bdk-python/scripts/generate-linux.sh b/bdk-python/scripts/generate-linux.sh index c8d5e713..b7f4efc4 100644 --- a/bdk-python/scripts/generate-linux.sh +++ b/bdk-python/scripts/generate-linux.sh @@ -1,11 +1,6 @@ #!/usr/bin/env bash -set -euo pipefail -${PYBIN}/python --version -${PYBIN}/pip install -r requirements.txt - cd ../bdk-ffi/ -rustup default 1.77.1 echo "Generating native binaries..." cargo build --profile release-smaller @@ -16,4 +11,12 @@ cargo run --bin uniffi-bindgen generate --library ./target/release-smaller/libbd echo "Copying linux libbdkffi.so..." cp ./target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so -echo "All done!" +cd ../bdk-python + +echo "Building wheel" + +python3 setup.py --verbose bdist_wheel + +# echo "Installing BDK Python" + +# pip3 install ./dist/bdkpython-1.0.0b2.dev0-cp310-cp310-linux_x86_64.whl --force-reinstall \ No newline at end of file