├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: ci 4 | 5 | jobs: 6 | 7 | build-test: 8 | name: Build 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | rust: 13 | - 1.50.0 # STABLE 14 | - 1.45.0 # MSRV 15 | steps: 16 | - name: checkout 17 | uses: actions/checkout@v2 18 | - name: Generate cache key 19 | run: echo "${{ matrix.rust }}" 20 | - name: Set default toolchain 21 | run: rustup default ${{ matrix.rust }} 22 | - name: Set profile 23 | run: rustup set profile minimal 24 | - name: Add clippy 25 | run: rustup component add clippy 26 | - name: Update toolchain 27 | run: rustup update 28 | - name: Build 29 | run: cargo build 30 | - name: Test 31 | run: cargo test 32 | - name: Clippy 33 | run: cargo clippy 34 | 35 | fmt: 36 | name: Rust fmt 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v2 41 | - name: Set default toolchain 42 | run: rustup default 1.50.0 # STABLE 43 | - name: Set profile 44 | run: rustup set profile minimal 45 | - name: Add clippy 46 | run: rustup component add rustfmt 47 | - name: Update toolchain 48 | run: rustup update 49 | - name: Check fmt 50 | run: cargo fmt --all -- --check 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "async-trait" 7 | version = "0.1.51" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" 10 | dependencies = [ 11 | "proc-macro2", 12 | "quote", 13 | "syn", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "0.1.7" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 21 | 22 | [[package]] 23 | name = "autocfg" 24 | version = "1.0.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 27 | 28 | [[package]] 29 | name = "base64" 30 | version = "0.10.1" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" 33 | dependencies = [ 34 | "byteorder", 35 | ] 36 | 37 | [[package]] 38 | name = "base64-compat" 39 | version = "1.0.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" 42 | dependencies = [ 43 | "byteorder", 44 | ] 45 | 46 | [[package]] 47 | name = "bdk" 48 | version = "0.11.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "5c7e8fe57edea4e7d6edcb90171060596a02175d349ff057115e1927dc6b0d3a" 51 | dependencies = [ 52 | "async-trait", 53 | "bdk-macros", 54 | "bitcoin 0.27.0", 55 | "electrum-client 0.8.0", 56 | "js-sys", 57 | "log", 58 | "miniscript 6.0.1", 59 | "rand 0.7.3", 60 | "serde", 61 | "serde_json", 62 | "sled", 63 | ] 64 | 65 | [[package]] 66 | name = "bdk-macros" 67 | version = "0.5.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "c3f510015e946c5995cc169f7ed4c92ba032bbce795c0956ee0d98d82f7aff78" 70 | dependencies = [ 71 | "proc-macro2", 72 | "quote", 73 | "syn", 74 | ] 75 | 76 | [[package]] 77 | name = "bech32" 78 | version = "0.7.3" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" 81 | 82 | [[package]] 83 | name = "bech32" 84 | version = "0.8.1" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" 87 | 88 | [[package]] 89 | name = "bip39" 90 | version = "1.0.1" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f" 93 | dependencies = [ 94 | "bitcoin_hashes 0.9.7", 95 | "rand_core 0.4.2", 96 | "serde", 97 | "unicode-normalization", 98 | ] 99 | 100 | [[package]] 101 | name = "bitcoin" 102 | version = "0.26.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "6742ec672d3f12506f4ac5c0d853926ff1f94e675f60ffd3224039972bf663f1" 105 | dependencies = [ 106 | "bech32 0.7.3", 107 | "bitcoin_hashes 0.9.7", 108 | "secp256k1", 109 | "serde", 110 | ] 111 | 112 | [[package]] 113 | name = "bitcoin" 114 | version = "0.27.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "8a427b27dae305157520d86673f2393b3eb08d880609abfcffc6e3c3c820e764" 117 | dependencies = [ 118 | "base64-compat", 119 | "bech32 0.8.1", 120 | "bitcoin_hashes 0.10.0", 121 | "secp256k1", 122 | "serde", 123 | ] 124 | 125 | [[package]] 126 | name = "bitcoin_hashes" 127 | version = "0.7.6" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "b375d62f341cef9cd9e77793ec8f1db3fc9ce2e4d57e982c8fe697a2c16af3b6" 130 | 131 | [[package]] 132 | name = "bitcoin_hashes" 133 | version = "0.9.7" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1" 136 | dependencies = [ 137 | "serde", 138 | ] 139 | 140 | [[package]] 141 | name = "bitcoin_hashes" 142 | version = "0.10.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0" 145 | dependencies = [ 146 | "serde", 147 | ] 148 | 149 | [[package]] 150 | name = "bitflags" 151 | version = "1.3.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 154 | 155 | [[package]] 156 | name = "bumpalo" 157 | version = "3.7.0" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 160 | 161 | [[package]] 162 | name = "byteorder" 163 | version = "1.4.3" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 166 | 167 | [[package]] 168 | name = "cc" 169 | version = "1.0.70" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" 172 | 173 | [[package]] 174 | name = "cfg-if" 175 | version = "1.0.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 178 | 179 | [[package]] 180 | name = "cloudabi" 181 | version = "0.0.3" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 184 | dependencies = [ 185 | "bitflags", 186 | ] 187 | 188 | [[package]] 189 | name = "crc32fast" 190 | version = "1.2.1" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 193 | dependencies = [ 194 | "cfg-if", 195 | ] 196 | 197 | [[package]] 198 | name = "crossbeam-epoch" 199 | version = "0.9.5" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" 202 | dependencies = [ 203 | "cfg-if", 204 | "crossbeam-utils", 205 | "lazy_static", 206 | "memoffset", 207 | "scopeguard", 208 | ] 209 | 210 | [[package]] 211 | name = "crossbeam-utils" 212 | version = "0.8.5" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 215 | dependencies = [ 216 | "cfg-if", 217 | "lazy_static", 218 | ] 219 | 220 | [[package]] 221 | name = "edk" 222 | version = "0.1.0" 223 | dependencies = [ 224 | "bdk", 225 | "bip39", 226 | "bitcoin 0.27.0", 227 | "electrum-client 0.7.0", 228 | "elements", 229 | "elements-miniscript", 230 | "miniscript 5.1.0", 231 | "serde", 232 | "serde_json", 233 | ] 234 | 235 | [[package]] 236 | name = "electrum-client" 237 | version = "0.7.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "4cab4d90cc575a7daab4cfed9e315912a88071bc47462e6be57516a2f01ccc89" 240 | dependencies = [ 241 | "bitcoin 0.26.2", 242 | "log", 243 | "rustls", 244 | "serde", 245 | "serde_json", 246 | "socks", 247 | "webpki", 248 | "webpki-roots", 249 | ] 250 | 251 | [[package]] 252 | name = "electrum-client" 253 | version = "0.8.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "edd12f125852d77980725243b2a8b3bea73cd4c7a22c33bc52b08b664c561dc7" 256 | dependencies = [ 257 | "bitcoin 0.27.0", 258 | "log", 259 | "rustls", 260 | "serde", 261 | "serde_json", 262 | "socks", 263 | "webpki", 264 | "webpki-roots", 265 | ] 266 | 267 | [[package]] 268 | name = "elements" 269 | version = "0.18.0" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "aa776445bd0ef9b30eab2e7c09cacc3a545e0502c178fdca9c4c6d80f738d519" 272 | dependencies = [ 273 | "bitcoin 0.27.0", 274 | "bitcoin_hashes 0.10.0", 275 | "secp256k1-zkp", 276 | "serde", 277 | "serde_json", 278 | "slip21", 279 | ] 280 | 281 | [[package]] 282 | name = "elements-miniscript" 283 | version = "0.1.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "cfc1fe1025a4d64d76bcc479685610a2d9c4903fdb1eb72c8bf4c2d1b3dbdc1a" 286 | dependencies = [ 287 | "bitcoin 0.27.0", 288 | "elements", 289 | "miniscript 6.0.1", 290 | ] 291 | 292 | [[package]] 293 | name = "fs2" 294 | version = "0.4.3" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 297 | dependencies = [ 298 | "libc", 299 | "winapi 0.3.9", 300 | ] 301 | 302 | [[package]] 303 | name = "fuchsia-cprng" 304 | version = "0.1.1" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 307 | 308 | [[package]] 309 | name = "fxhash" 310 | version = "0.2.1" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 313 | dependencies = [ 314 | "byteorder", 315 | ] 316 | 317 | [[package]] 318 | name = "getrandom" 319 | version = "0.1.16" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 322 | dependencies = [ 323 | "cfg-if", 324 | "js-sys", 325 | "libc", 326 | "wasi", 327 | "wasm-bindgen", 328 | ] 329 | 330 | [[package]] 331 | name = "instant" 332 | version = "0.1.10" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 335 | dependencies = [ 336 | "cfg-if", 337 | ] 338 | 339 | [[package]] 340 | name = "itoa" 341 | version = "0.4.8" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 344 | 345 | [[package]] 346 | name = "js-sys" 347 | version = "0.3.53" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" 350 | dependencies = [ 351 | "wasm-bindgen", 352 | ] 353 | 354 | [[package]] 355 | name = "lazy_static" 356 | version = "1.4.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 359 | 360 | [[package]] 361 | name = "libc" 362 | version = "0.2.101" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" 365 | 366 | [[package]] 367 | name = "lock_api" 368 | version = "0.4.5" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 371 | dependencies = [ 372 | "scopeguard", 373 | ] 374 | 375 | [[package]] 376 | name = "log" 377 | version = "0.4.14" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 380 | dependencies = [ 381 | "cfg-if", 382 | ] 383 | 384 | [[package]] 385 | name = "maybe-uninit" 386 | version = "2.0.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 389 | 390 | [[package]] 391 | name = "memoffset" 392 | version = "0.6.4" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 395 | dependencies = [ 396 | "autocfg 1.0.1", 397 | ] 398 | 399 | [[package]] 400 | name = "miniscript" 401 | version = "5.1.0" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "71f455be59a359d50370c4f587afbc5739c862e684c5afecae80ab93e7474b4e" 404 | dependencies = [ 405 | "bitcoin 0.26.2", 406 | ] 407 | 408 | [[package]] 409 | name = "miniscript" 410 | version = "6.0.1" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "d69450033bf162edf854d4aacaff82ca5ef34fa81f6cf69e1c81a103f0834997" 413 | dependencies = [ 414 | "bitcoin 0.27.0", 415 | ] 416 | 417 | [[package]] 418 | name = "once_cell" 419 | version = "1.8.0" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 422 | 423 | [[package]] 424 | name = "parking_lot" 425 | version = "0.11.2" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 428 | dependencies = [ 429 | "instant", 430 | "lock_api", 431 | "parking_lot_core", 432 | ] 433 | 434 | [[package]] 435 | name = "parking_lot_core" 436 | version = "0.8.5" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 439 | dependencies = [ 440 | "cfg-if", 441 | "instant", 442 | "libc", 443 | "redox_syscall", 444 | "smallvec 1.6.1", 445 | "winapi 0.3.9", 446 | ] 447 | 448 | [[package]] 449 | name = "ppv-lite86" 450 | version = "0.2.10" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 453 | 454 | [[package]] 455 | name = "proc-macro2" 456 | version = "1.0.29" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 459 | dependencies = [ 460 | "unicode-xid", 461 | ] 462 | 463 | [[package]] 464 | name = "quote" 465 | version = "1.0.9" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 468 | dependencies = [ 469 | "proc-macro2", 470 | ] 471 | 472 | [[package]] 473 | name = "rand" 474 | version = "0.6.5" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 477 | dependencies = [ 478 | "autocfg 0.1.7", 479 | "libc", 480 | "rand_chacha 0.1.1", 481 | "rand_core 0.4.2", 482 | "rand_hc 0.1.0", 483 | "rand_isaac", 484 | "rand_jitter", 485 | "rand_os", 486 | "rand_pcg", 487 | "rand_xorshift", 488 | "winapi 0.3.9", 489 | ] 490 | 491 | [[package]] 492 | name = "rand" 493 | version = "0.7.3" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 496 | dependencies = [ 497 | "getrandom", 498 | "libc", 499 | "rand_chacha 0.2.2", 500 | "rand_core 0.5.1", 501 | "rand_hc 0.2.0", 502 | ] 503 | 504 | [[package]] 505 | name = "rand_chacha" 506 | version = "0.1.1" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 509 | dependencies = [ 510 | "autocfg 0.1.7", 511 | "rand_core 0.3.1", 512 | ] 513 | 514 | [[package]] 515 | name = "rand_chacha" 516 | version = "0.2.2" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 519 | dependencies = [ 520 | "ppv-lite86", 521 | "rand_core 0.5.1", 522 | ] 523 | 524 | [[package]] 525 | name = "rand_core" 526 | version = "0.3.1" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 529 | dependencies = [ 530 | "rand_core 0.4.2", 531 | ] 532 | 533 | [[package]] 534 | name = "rand_core" 535 | version = "0.4.2" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 538 | 539 | [[package]] 540 | name = "rand_core" 541 | version = "0.5.1" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 544 | dependencies = [ 545 | "getrandom", 546 | ] 547 | 548 | [[package]] 549 | name = "rand_hc" 550 | version = "0.1.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 553 | dependencies = [ 554 | "rand_core 0.3.1", 555 | ] 556 | 557 | [[package]] 558 | name = "rand_hc" 559 | version = "0.2.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 562 | dependencies = [ 563 | "rand_core 0.5.1", 564 | ] 565 | 566 | [[package]] 567 | name = "rand_isaac" 568 | version = "0.1.1" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 571 | dependencies = [ 572 | "rand_core 0.3.1", 573 | ] 574 | 575 | [[package]] 576 | name = "rand_jitter" 577 | version = "0.1.4" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 580 | dependencies = [ 581 | "libc", 582 | "rand_core 0.4.2", 583 | "winapi 0.3.9", 584 | ] 585 | 586 | [[package]] 587 | name = "rand_os" 588 | version = "0.1.3" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 591 | dependencies = [ 592 | "cloudabi", 593 | "fuchsia-cprng", 594 | "libc", 595 | "rand_core 0.4.2", 596 | "rdrand", 597 | "winapi 0.3.9", 598 | ] 599 | 600 | [[package]] 601 | name = "rand_pcg" 602 | version = "0.1.2" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 605 | dependencies = [ 606 | "autocfg 0.1.7", 607 | "rand_core 0.4.2", 608 | ] 609 | 610 | [[package]] 611 | name = "rand_xorshift" 612 | version = "0.1.1" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 615 | dependencies = [ 616 | "rand_core 0.3.1", 617 | ] 618 | 619 | [[package]] 620 | name = "rdrand" 621 | version = "0.4.0" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 624 | dependencies = [ 625 | "rand_core 0.3.1", 626 | ] 627 | 628 | [[package]] 629 | name = "redox_syscall" 630 | version = "0.2.10" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 633 | dependencies = [ 634 | "bitflags", 635 | ] 636 | 637 | [[package]] 638 | name = "ring" 639 | version = "0.16.20" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 642 | dependencies = [ 643 | "cc", 644 | "libc", 645 | "once_cell", 646 | "spin", 647 | "untrusted", 648 | "web-sys", 649 | "winapi 0.3.9", 650 | ] 651 | 652 | [[package]] 653 | name = "rustls" 654 | version = "0.16.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" 657 | dependencies = [ 658 | "base64", 659 | "log", 660 | "ring", 661 | "sct", 662 | "webpki", 663 | ] 664 | 665 | [[package]] 666 | name = "ryu" 667 | version = "1.0.5" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 670 | 671 | [[package]] 672 | name = "scopeguard" 673 | version = "1.1.0" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 676 | 677 | [[package]] 678 | name = "sct" 679 | version = "0.6.1" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" 682 | dependencies = [ 683 | "ring", 684 | "untrusted", 685 | ] 686 | 687 | [[package]] 688 | name = "secp256k1" 689 | version = "0.20.3" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" 692 | dependencies = [ 693 | "bitcoin_hashes 0.9.7", 694 | "rand 0.6.5", 695 | "secp256k1-sys", 696 | "serde", 697 | ] 698 | 699 | [[package]] 700 | name = "secp256k1-sys" 701 | version = "0.4.1" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b" 704 | dependencies = [ 705 | "cc", 706 | ] 707 | 708 | [[package]] 709 | name = "secp256k1-zkp" 710 | version = "0.4.0" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "5e57b977141f57b224fc579c23dac5978b5f594281450fe8b5e2b6aa816d0fde" 713 | dependencies = [ 714 | "bitcoin_hashes 0.9.7", 715 | "rand 0.6.5", 716 | "secp256k1", 717 | "secp256k1-zkp-sys", 718 | "serde", 719 | ] 720 | 721 | [[package]] 722 | name = "secp256k1-zkp-sys" 723 | version = "0.4.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "b45a3c4e1291985397df9770f864ed88bd51ee684b13a384f49d2fedb29f86f0" 726 | dependencies = [ 727 | "cc", 728 | "secp256k1-sys", 729 | ] 730 | 731 | [[package]] 732 | name = "serde" 733 | version = "1.0.130" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 736 | dependencies = [ 737 | "serde_derive", 738 | ] 739 | 740 | [[package]] 741 | name = "serde_derive" 742 | version = "1.0.130" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 745 | dependencies = [ 746 | "proc-macro2", 747 | "quote", 748 | "syn", 749 | ] 750 | 751 | [[package]] 752 | name = "serde_json" 753 | version = "1.0.44" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" 756 | dependencies = [ 757 | "itoa", 758 | "ryu", 759 | "serde", 760 | ] 761 | 762 | [[package]] 763 | name = "sled" 764 | version = "0.34.6" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "1d0132f3e393bcb7390c60bb45769498cf4550bcb7a21d7f95c02b69f6362cdc" 767 | dependencies = [ 768 | "crc32fast", 769 | "crossbeam-epoch", 770 | "crossbeam-utils", 771 | "fs2", 772 | "fxhash", 773 | "libc", 774 | "log", 775 | "parking_lot", 776 | ] 777 | 778 | [[package]] 779 | name = "slip21" 780 | version = "0.2.0" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "acff9a1a0f4d902c8184bdde5338861dd95eb1397a12c4df78fb0b6ebeed4cbe" 783 | dependencies = [ 784 | "bitcoin_hashes 0.7.6", 785 | ] 786 | 787 | [[package]] 788 | name = "smallvec" 789 | version = "0.6.14" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" 792 | dependencies = [ 793 | "maybe-uninit", 794 | ] 795 | 796 | [[package]] 797 | name = "smallvec" 798 | version = "1.6.1" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 801 | 802 | [[package]] 803 | name = "socks" 804 | version = "0.3.3" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "30f86c7635fadf2814201a4f67efefb0007588ae7422ce299f354ab5c97f61ae" 807 | dependencies = [ 808 | "byteorder", 809 | "libc", 810 | "winapi 0.2.8", 811 | "ws2_32-sys", 812 | ] 813 | 814 | [[package]] 815 | name = "spin" 816 | version = "0.5.2" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 819 | 820 | [[package]] 821 | name = "syn" 822 | version = "1.0.76" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" 825 | dependencies = [ 826 | "proc-macro2", 827 | "quote", 828 | "unicode-xid", 829 | ] 830 | 831 | [[package]] 832 | name = "unicode-normalization" 833 | version = "0.1.9" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" 836 | dependencies = [ 837 | "smallvec 0.6.14", 838 | ] 839 | 840 | [[package]] 841 | name = "unicode-xid" 842 | version = "0.2.2" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 845 | 846 | [[package]] 847 | name = "untrusted" 848 | version = "0.7.1" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 851 | 852 | [[package]] 853 | name = "wasi" 854 | version = "0.9.0+wasi-snapshot-preview1" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 857 | 858 | [[package]] 859 | name = "wasm-bindgen" 860 | version = "0.2.76" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" 863 | dependencies = [ 864 | "cfg-if", 865 | "wasm-bindgen-macro", 866 | ] 867 | 868 | [[package]] 869 | name = "wasm-bindgen-backend" 870 | version = "0.2.76" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" 873 | dependencies = [ 874 | "bumpalo", 875 | "lazy_static", 876 | "log", 877 | "proc-macro2", 878 | "quote", 879 | "syn", 880 | "wasm-bindgen-shared", 881 | ] 882 | 883 | [[package]] 884 | name = "wasm-bindgen-macro" 885 | version = "0.2.76" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" 888 | dependencies = [ 889 | "quote", 890 | "wasm-bindgen-macro-support", 891 | ] 892 | 893 | [[package]] 894 | name = "wasm-bindgen-macro-support" 895 | version = "0.2.76" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" 898 | dependencies = [ 899 | "proc-macro2", 900 | "quote", 901 | "syn", 902 | "wasm-bindgen-backend", 903 | "wasm-bindgen-shared", 904 | ] 905 | 906 | [[package]] 907 | name = "wasm-bindgen-shared" 908 | version = "0.2.76" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" 911 | 912 | [[package]] 913 | name = "web-sys" 914 | version = "0.3.53" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c" 917 | dependencies = [ 918 | "js-sys", 919 | "wasm-bindgen", 920 | ] 921 | 922 | [[package]] 923 | name = "webpki" 924 | version = "0.21.4" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" 927 | dependencies = [ 928 | "ring", 929 | "untrusted", 930 | ] 931 | 932 | [[package]] 933 | name = "webpki-roots" 934 | version = "0.19.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739" 937 | dependencies = [ 938 | "webpki", 939 | ] 940 | 941 | [[package]] 942 | name = "winapi" 943 | version = "0.2.8" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 946 | 947 | [[package]] 948 | name = "winapi" 949 | version = "0.3.9" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 952 | dependencies = [ 953 | "winapi-i686-pc-windows-gnu", 954 | "winapi-x86_64-pc-windows-gnu", 955 | ] 956 | 957 | [[package]] 958 | name = "winapi-build" 959 | version = "0.1.1" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 962 | 963 | [[package]] 964 | name = "winapi-i686-pc-windows-gnu" 965 | version = "0.4.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 968 | 969 | [[package]] 970 | name = "winapi-x86_64-pc-windows-gnu" 971 | version = "0.4.0" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 974 | 975 | [[package]] 976 | name = "ws2_32-sys" 977 | version = "0.2.1" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 980 | dependencies = [ 981 | "winapi 0.2.8", 982 | "winapi-build", 983 | ] 984 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edk" 3 | description = "Elements Dev Kit" 4 | version = "0.1.0" 5 | authors = ["Luca Vaccaro "] 6 | 7 | [dependencies] 8 | elements = { version = "0.18.0", features = ["serde-feature"] } 9 | elements-miniscript = { version = "0.1.0" } 10 | bitcoin = { version = "0.27", features = [ "use-serde", "rand" ] } 11 | bdk = { version = "0.11.0" } 12 | miniscript = { version = "5.1.0" } 13 | electrum-client = { version = "0.7", optional = true } 14 | bip39 = { version = "1.0.1" } 15 | serde = { version = "1.0", features = ["derive"] } 16 | serde_json = "1.0" 17 | 18 | [features] 19 | electrum = ["electrum-client"] 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

EDK

3 | 4 |

5 | Elements Dev Kit 6 |

7 | 8 |

9 | A modern, lightweight, descriptor-based wallet library for Elements / Liquid written in Rust! 10 |
11 | Inspired by BDK for Elements & Liquid 12 |

13 | 14 |

15 | CI Status 16 |

17 | 18 |
19 | 20 | ## About 21 | 22 | It uses Elements Miniscript to support descriptors with generalized conditions. 23 | 24 | Based on: 25 | - [rust-elements](https://github.com/ElementsProject/rust-elements/): Library with support for de/serialization, parsing and executing on data structures and network messages related to Elements 26 | - [rust-miniscript-elements](https://github.com/sanket1729/rust-miniscript-elements): Library for handling Miniscript, which is a subset of Elements Script designed to support simple and general tooling. 27 | - [bdk](https://github.com/bitcoindevkit/bdk): A modern, lightweight, descriptor-based wallet library written in Rust. 28 | 29 | 30 | ## Examples 31 | 32 | ### Sync the balance of a descriptor 33 | 34 | ```rust,no_run 35 | let database = MemoryDatabase::new(); 36 | let client = Client::new("ssl://blockstream.info:995").unwrap(); 37 | let wallet = Wallet::new(descriptor, master_blinding_key, database, client).unwrap(); 38 | let balance = wallet.balance().unwrap(); 39 | println!("AssetId: Value"); 40 | for b in balance { 41 | println!("{}: {}", b.0, b.1); 42 | } 43 | ``` 44 | 45 | ### Generate a few addresses 46 | 47 | ```rust,no_run 48 | let database = MemoryDatabase::new(); 49 | let client = Client::new("ssl://blockstream.info:995").unwrap(); 50 | let wallet = Wallet::new(descriptor, master_blinding_key, database, client).unwrap(); 51 | println!("Address #0: {}", wallet.get_new_address()?); 52 | println!("Address #1: {}", wallet.get_new_address()?); 53 | println!("Address #2: {}", wallet.get_new_address()?); 54 | ``` 55 | 56 | ## Testing 57 | 58 | ### Unit testing 59 | 60 | ``` 61 | cargo test 62 | ``` 63 | 64 | ## License 65 | 66 | Licensed under either of 67 | 68 | * Apache License, Version 2.0 69 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 70 | * MIT license 71 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 72 | 73 | at your option. 74 | 75 | ## Contribution 76 | 77 | Unless you explicitly state otherwise, any contribution intentionally submitted 78 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 79 | dual licensed as above, without any additional terms or conditions. 80 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::{HashMap, HashSet}; 3 | use std::error; 4 | use std::fmt; 5 | use std::str::FromStr; 6 | 7 | pub extern crate bdk; 8 | pub extern crate bip39; 9 | pub extern crate elements_miniscript as miniscript; 10 | pub extern crate serde; 11 | 12 | use miniscript::bitcoin::network::constants::Network; 13 | use miniscript::bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey}; 14 | use miniscript::elements::confidential::{ 15 | self, Asset, AssetBlindingFactor, Nonce, Value, ValueBlindingFactor, 16 | }; 17 | use miniscript::elements::encode::deserialize as elm_des; 18 | use miniscript::elements::encode::serialize as elm_ser; 19 | use miniscript::elements::secp256k1_zkp::{All, PublicKey, Secp256k1}; 20 | use miniscript::elements::slip77::MasterBlindingKey; 21 | use miniscript::elements::TxOutSecrets; 22 | use miniscript::elements::{Address, AddressParams}; 23 | use miniscript::{Descriptor, DescriptorPublicKey, DescriptorTrait, TranslatePk2}; 24 | 25 | use bdk::blockchain::Blockchain; 26 | use bdk::database::memory::MemoryDatabase; 27 | use bdk::database::{BatchDatabase, BatchOperations, Database}; 28 | use bdk::electrum_client::{ 29 | Client, ConfigBuilder, ElectrumApi, GetHistoryRes, ListUnspentRes, Socks5Config, 30 | }; 31 | use serde::{Deserialize, Serialize}; 32 | 33 | pub enum ScriptType { 34 | P2shP2wpkh = 0, 35 | P2wpkh = 1, 36 | P2pkh = 2, 37 | } 38 | 39 | #[derive(Default)] 40 | pub struct DownloadTxResult { 41 | pub txs: Vec<(elements::Txid, elements::Transaction)>, 42 | pub unblinds: Vec<(elements::OutPoint, elements::TxOutSecrets)>, 43 | } 44 | 45 | pub struct Wallet { 46 | descriptor: Descriptor, 47 | master_blinding_key: MasterBlindingKey, 48 | secp: Secp256k1, 49 | client: Client, 50 | database: RefCell, 51 | network: &'static AddressParams, 52 | } 53 | 54 | impl Wallet 55 | where 56 | D: BatchDatabase, 57 | { 58 | pub fn new( 59 | descriptor: Descriptor, 60 | master_blinding_key: MasterBlindingKey, 61 | database: D, 62 | client: Client, 63 | network: &'static AddressParams, 64 | ) -> Result { 65 | Ok(Wallet { 66 | descriptor, 67 | master_blinding_key, 68 | secp: Secp256k1::new(), 69 | client, 70 | database: RefCell::new(database), 71 | network, 72 | }) 73 | } 74 | 75 | /// Get the Liquid network the wallet is using. 76 | pub fn network(&self) -> &'static AddressParams { 77 | self.network 78 | } 79 | 80 | /// Return a reference to the internal blockchain client 81 | pub fn client(&self) -> &Client { 82 | &self.client 83 | } 84 | 85 | fn get_address(&self, index: u32) -> Result { 86 | let xpk = self 87 | .descriptor 88 | .derive(index) 89 | .translate_pk2(|xpk| xpk.derive_public_key(&self.secp)) 90 | .unwrap(); 91 | let unconfidential_address = xpk.address(&self.network).unwrap(); 92 | let script_pubkey = xpk.script_pubkey(); 93 | let blinding_sk = self.master_blinding_key.derive_blinding_key(&script_pubkey); 94 | let blinding_pk = PublicKey::from_secret_key(&self.secp, &blinding_sk); 95 | let confidential_address = unconfidential_address.to_confidential(blinding_pk); 96 | Ok(confidential_address) 97 | } 98 | 99 | // Return a newly derived address using the external descriptor 100 | pub fn get_new_address(&self) -> Result { 101 | let index = match self.descriptor.is_deriveable() { 102 | false => 0, 103 | true => self 104 | .database 105 | .borrow_mut() 106 | .increment_last_index(bdk::KeychainKind::External)?, 107 | }; 108 | let addr = self.get_address(index)?; 109 | Ok(addr) 110 | } 111 | 112 | fn get_previous_addresses(&self) -> Result, bdk::Error> { 113 | let mut addresses = vec![]; 114 | for i in 0..self 115 | .database 116 | .borrow() 117 | .get_last_index(bdk::KeychainKind::External)? 118 | .unwrap_or(0) 119 | + 1 120 | { 121 | addresses.push(self.get_address(i)?); 122 | //println!("{} {} {}",i, self.get_address(i)?, self.database.get_last_index(bdk::KeychainKind::External)?.unwrap_or(0)); 123 | } 124 | Ok(addresses) 125 | } 126 | 127 | pub fn is_mine_address(&self, addr: &Address) -> Result { 128 | Ok(self.get_previous_addresses()?.contains(addr)) 129 | } 130 | 131 | pub fn balance(&self) -> Result, bdk::Error> { 132 | let addrs: Vec
= self.get_previous_addresses()?; 133 | let mut balances = HashMap::new(); 134 | 135 | for unblind in self.balance_addresses(addrs)?.unblinds { 136 | let tx_out = unblind.1; 137 | *balances.entry(tx_out.asset.to_string()).or_insert(0) += tx_out.value; 138 | } 139 | Ok(balances) 140 | } 141 | 142 | pub fn balance_addresses(&self, addrs: Vec
) -> Result { 143 | //let client = Client::new("ssl://blockstream.info:995").unwrap(); 144 | 145 | let mut history_txs_id = HashSet::::new(); 146 | let mut heights_set = HashSet::new(); 147 | let mut txid_height = HashMap::::new(); 148 | 149 | let scripts: Vec = addrs 150 | .iter() 151 | .map(|x| x.script_pubkey().into_bytes()) 152 | .map(|x| elements::Script::from(x)) 153 | .collect(); 154 | let b_scripts: Vec = addrs 155 | .iter() 156 | .map(|x| x.script_pubkey().into_bytes()) 157 | .map(|x| bitcoin::Script::from(x)) 158 | .collect(); 159 | let result: Vec> = self 160 | .client 161 | .batch_script_get_history(b_scripts.iter()) 162 | .unwrap(); 163 | let flattened: Vec = result.into_iter().flatten().collect(); 164 | for el in flattened { 165 | // el.height = -1 means unconfirmed with unconfirmed parents 166 | // el.height = 0 means unconfirmed with confirmed parents 167 | // but we threat those tx the same 168 | let height = el.height.max(0); 169 | heights_set.insert(height as u32); 170 | let tx = elements::Txid::from_hash(el.tx_hash.as_hash()); 171 | if height == 0 { 172 | txid_height.insert(tx, None); 173 | } else { 174 | txid_height.insert(tx, Some(height as u32)); 175 | } 176 | history_txs_id.insert(tx); 177 | } 178 | Ok(self.download_txs(&history_txs_id, &scripts)?) 179 | } 180 | 181 | fn download_txs( 182 | &self, 183 | history_txs_id: &HashSet, 184 | scripts: &Vec, 185 | ) -> Result { 186 | let mut txs = vec![]; 187 | let mut unblinds = vec![]; 188 | // BETxid has to be converted into bitcoin::Txid for rust-electrum-client 189 | let txs_to_download: Vec = history_txs_id 190 | .iter() 191 | .map(|x| bitcoin::Txid::from_hash(x.as_hash())) 192 | .collect(); 193 | if txs_to_download.is_empty() { 194 | Ok(DownloadTxResult::default()) 195 | } else { 196 | let txs_bytes_downloaded = self 197 | .client 198 | .batch_transaction_get_raw(txs_to_download.iter()) 199 | .unwrap(); 200 | let mut txs_downloaded: Vec = vec![]; 201 | for vec in txs_bytes_downloaded { 202 | let tx: elements::Transaction = elm_des(&vec).unwrap(); 203 | txs_downloaded.push(tx); 204 | } 205 | println!("txs_downloaded {}", txs_downloaded.len()); 206 | //let mut previous_txs_to_download = HashSet::new(); 207 | for tx in txs_downloaded.into_iter() { 208 | let txid = tx.txid(); 209 | println!("compute OutPoint Unblinded"); 210 | for (i, output) in tx.output.iter().enumerate() { 211 | let script = output.script_pubkey.clone(); 212 | if scripts.contains(&script) { 213 | let vout = i as u32; 214 | let outpoint = elements::OutPoint { 215 | txid: tx.txid(), 216 | vout, 217 | }; 218 | match self.try_unblind(outpoint, output.clone()) { 219 | Ok(unblinded) => unblinds.push((outpoint, unblinded)), 220 | Err(_) => println!("{} cannot unblind, ignoring (could be sender messed up with the blinding process)", outpoint), 221 | } 222 | } 223 | } 224 | txs.push((txid, tx)); 225 | } 226 | 227 | Ok(DownloadTxResult { txs, unblinds }) 228 | } 229 | } 230 | 231 | pub fn try_unblind( 232 | &self, 233 | outpoint: elements::OutPoint, 234 | output: elements::TxOut, 235 | ) -> Result { 236 | match (output.asset, output.value, output.nonce) { 237 | ( 238 | Asset::Confidential(_), 239 | confidential::Value::Confidential(_), 240 | Nonce::Confidential(_), 241 | ) => { 242 | let script = output.script_pubkey.clone(); 243 | let blinding_sk = self.master_blinding_key.derive_blinding_key(&script); 244 | //let blinding_pk = PublicKey::from_secret_key(&self.secp, &blinding_sk); 245 | let tx_out_secrets = output.unblind(&self.secp, blinding_sk).unwrap(); 246 | //println!("Unblinded outpoint:{} asset:{} value:{}", outpoint, tx_out_secrets.asset.to_string(), tx_out_secrets.value); 247 | Ok(tx_out_secrets) 248 | } 249 | (Asset::Explicit(asset_id), confidential::Value::Explicit(satoshi), _) => { 250 | let asset_bf = AssetBlindingFactor::from_slice(&[0u8; 32]).unwrap(); 251 | let value_bf = ValueBlindingFactor::from_slice(&[0u8; 32]).unwrap(); 252 | let tx_out_secrets = TxOutSecrets { 253 | asset: asset_id, 254 | asset_bf: asset_bf, 255 | value: satoshi, 256 | value_bf: value_bf, 257 | }; 258 | Ok(tx_out_secrets) 259 | } 260 | _ => Err(bdk::Error::Generic("Unexpected asset/value/nonce".into())), 261 | } 262 | } 263 | } 264 | 265 | fn main() {} 266 | 267 | #[cfg(test)] 268 | pub mod test { 269 | 270 | use super::*; 271 | 272 | fn aqua_subaccount_derivation( 273 | seed: &[u8], 274 | bip32_account_num: u32, 275 | script_type: &ScriptType, 276 | purpose: u32, 277 | ) -> Result { 278 | let secp = Secp256k1::new(); 279 | let network = bitcoin::network::constants::Network::Bitcoin; 280 | let master_xprv = ExtendedPrivKey::new_master(network, &seed).unwrap(); 281 | let master_xpub = ExtendedPubKey::from_private(&secp, &master_xprv); 282 | let master_blinding_key = MasterBlindingKey::new(&seed); 283 | 284 | // derive subaccount xprv & xpub 285 | let coin_type = 1776; // for liquid 286 | let path: bitcoin::util::bip32::DerivationPath = 287 | format!("m/{}'/{}'/{}'", purpose, coin_type, bip32_account_num) 288 | .parse() 289 | .unwrap(); 290 | let xprv = master_xprv.derive_priv(&secp, &path).unwrap(); 291 | let xpub = ExtendedPubKey::from_private(&secp, &xprv); 292 | 293 | // get internal chain for not change address 294 | let chain = xpub.ckd_pub(&secp, 0.into()).unwrap(); 295 | Ok(chain) 296 | } 297 | 298 | fn aqua_address_derivation( 299 | chain: bitcoin::util::bip32::ExtendedPubKey, 300 | index: u32, 301 | script_type: &ScriptType, 302 | master_blinding_key: &MasterBlindingKey, 303 | ) -> Result { 304 | let secp = Secp256k1::new(); 305 | let child_key = chain.ckd_pub(&secp, index.into()).unwrap().public_key; 306 | let params: &'static AddressParams = &AddressParams::LIQUID; 307 | let address = match script_type { 308 | ScriptType::P2pkh => Address::p2pkh(&child_key, None, params), 309 | ScriptType::P2shP2wpkh => Address::p2shwpkh(&child_key, None, params), 310 | ScriptType::P2wpkh => Address::p2wpkh(&child_key, None, params), 311 | }; 312 | let script_pubkey = address.script_pubkey(); 313 | //println!("xpk {}", &script_pubkey.to_string()); 314 | let blinding_sk = master_blinding_key.derive_blinding_key(&script_pubkey); 315 | let blinding_pk = PublicKey::from_secret_key(&secp, &blinding_sk); 316 | let confidential_address = address.to_confidential(blinding_pk); 317 | Ok(confidential_address) 318 | } 319 | 320 | fn aqua_address_derivation_descriptor( 321 | chain: bitcoin::util::bip32::ExtendedPubKey, 322 | index: u32, 323 | script_type: &ScriptType, 324 | master_blinding_key: &MasterBlindingKey, 325 | ) -> Result { 326 | let secp = Secp256k1::new(); 327 | let desc = format!("{}({}/*)", "elwpkh", &chain.to_string()); 328 | 329 | let descriptor = Descriptor::::from_str(&desc).unwrap(); 330 | let xpk = descriptor 331 | .derive(1) 332 | .translate_pk2(|xpk| xpk.derive_public_key(&secp)) 333 | .unwrap(); 334 | //println!("xpk {}", &xpk.script_pubkey().to_string()); 335 | let unconfidential_address = xpk.address(&AddressParams::LIQUID).unwrap(); 336 | let script_pubkey = xpk.script_pubkey(); 337 | let blinding_sk = master_blinding_key.derive_blinding_key(&script_pubkey); 338 | let blinding_pk = PublicKey::from_secret_key(&secp, &blinding_sk); 339 | let confidential_address = unconfidential_address.to_confidential(blinding_pk); 340 | Ok(confidential_address) 341 | } 342 | 343 | #[test] 344 | fn test_aqua_derivation() { 345 | let mnemonic = "fashion twist awful letter neck creek giggle april head once taxi arena"; 346 | let seed = bip39::Mnemonic::from_str(mnemonic) 347 | .unwrap() 348 | .to_seed_normalized(""); 349 | 350 | let master_blinding_key = MasterBlindingKey::new(&seed); 351 | assert_eq!( 352 | "8303cf4dc264b1c8ba08114c9b0441f5b50f147f2c09499c4cb4a346b5301d55", 353 | master_blinding_key.0.to_string() 354 | ); 355 | 356 | // 0="p2sh-p2wpkh" 1="p2wpkh" 2="p2pkh" , 49="p2sh-p2wpkh" 84="p2wpkh" 44="p2pkh" 357 | // let (bip32_account_num, script_type, purpose) = (0, ScriptType::P2shP2wpkh, 49);let chain = aqua_subaccount_derivation(&seed, bip32_account_num, &script_type, purpose).unwrap(); 358 | 359 | let (bip32_account, script_type, purpose) = (1, ScriptType::P2wpkh, 84); 360 | let chain = 361 | aqua_subaccount_derivation(&seed, bip32_account, &script_type, purpose).unwrap(); 362 | assert_eq!( 363 | "xpub6EbiTuUXu4RbdT3gS2CXEofcLwoN5gJmmZeL5cDPUcTvtTF1Zezj2H3zqazqAxsJACJpvWE4JeAS8aN6fcdZVsLBSCDAxPcid4azopa8Aq8", 364 | &chain.to_string() 365 | ); 366 | 367 | let addr = aqua_address_derivation(chain, 1, &script_type, &master_blinding_key).unwrap(); 368 | assert_eq!( 369 | "lq1qq2s7q8hc389g09sgy85gvs6d68u778fwh574tuhu87jszza07ekt2v4kzh4kch42jh06v56yux4mzhvtulwvurgd0f8xh9mzn", 370 | &addr.to_string() 371 | ); 372 | 373 | let addr = aqua_address_derivation_descriptor(chain, 1, &script_type, &master_blinding_key) 374 | .unwrap(); 375 | assert_eq!( 376 | "lq1qq2s7q8hc389g09sgy85gvs6d68u778fwh574tuhu87jszza07ekt2v4kzh4kch42jh06v56yux4mzhvtulwvurgd0f8xh9mzn", 377 | &addr.to_string() 378 | ); 379 | 380 | let desc = format!("{}({}/*)", "elwpkh", &chain.to_string()); 381 | let descriptor = Descriptor::::from_str(&desc).unwrap(); 382 | assert_eq!( 383 | "elwpkh(xpub6F33eZ1QWddkNKw27gdgACBGorYVU4iqJQwMDL85jVeiZKSjFbnKhJr15DtzBuiDLHAEr2aXk2aXahLq8Jpt9KZh1ubHuCc9Nbf65d65kPH/*)#yvsg4jzf", 384 | &descriptor.to_string() 385 | ); 386 | 387 | let database = MemoryDatabase::new(); 388 | 389 | let client = Client::new("ssl://blockstream.info:995").unwrap(); 390 | let mut wallet = Wallet::new( 391 | descriptor, 392 | master_blinding_key, 393 | MemoryDatabase::new(), 394 | client, 395 | &AddressParams::LIQUID, 396 | ) 397 | .unwrap(); 398 | let addr = wallet.get_new_address().unwrap(); 399 | let addr = wallet.get_new_address().unwrap(); 400 | //let addr2 = wallet.get_new_address().unwrap(); 401 | assert_eq!( 402 | "lq1qqwygr58ye8fs69lrweqpj20lal8nyqxpygpqw5er4p6gsazdcqpzd0yf4xcrlme85chya4tsmrwdrgrgt92gchw9fvzy5r5m3", 403 | &addr.to_string() 404 | ); 405 | 406 | let mine = wallet.is_mine_address(&addr).unwrap(); 407 | assert!(mine); 408 | 409 | /*let balance = wallet.balance().unwrap(); 410 | println!("AssetId: Value"); 411 | for b in balance { 412 | println!("{}: {}", b.0, b.1); 413 | }*/ 414 | } 415 | } 416 | --------------------------------------------------------------------------------