From 96d3c336f6b42bf41ba71b0077705dd8e0e9a934 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Tue, 16 Dec 2025 14:46:54 +0200 Subject: [PATCH 01/39] add the basic daemon --- .gitignore | 1 + Cargo.lock | 1003 +++++++++++++++++++++++++++++---------------- Cargo.toml | 4 + src/daemon/mod.rs | 86 ++++ src/lib.rs | 3 + 5 files changed, 739 insertions(+), 358 deletions(-) create mode 100644 src/daemon/mod.rs diff --git a/.gitignore b/.gitignore index 2f7896d..c8a5150 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +rust-toolchain.toml diff --git a/Cargo.lock b/Cargo.lock index 61ca7bb..62f0da1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,13 +4,22 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -26,28 +35,28 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ + "hermit-abi", "libc", - "termion", "winapi", ] [[package]] name = "autocfg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" - -[[package]] -name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base58ck" @@ -55,8 +64,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals 0.3.0", - "bitcoin_hashes 0.14.0", + "bitcoin-internals", + "bitcoin_hashes", ] [[package]] @@ -76,52 +85,40 @@ dependencies = [ [[package]] name = "bech32" -version = "0.9.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bech32" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "bip39" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes 0.13.0", + "bitcoin_hashes", "serde", "unicode-normalization", ] [[package]] name = "bitcoin" -version = "0.32.6" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8929a18b8e33ea6b3c09297b687baaa71fb1b97353243a3f1029fad5c59c5b" +checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" dependencies = [ "base58ck", "base64", - "bech32 0.11.0", - "bitcoin-internals 0.3.0", + "bech32", + "bitcoin-internals", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes 0.14.0", - "hex-conservative 0.2.1", + "bitcoin_hashes", + "hex-conservative", "hex_lit", "secp256k1", "serde", ] -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - [[package]] name = "bitcoin-internals" version = "0.3.0" @@ -133,9 +130,9 @@ dependencies = [ [[package]] name = "bitcoin-io" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-private" @@ -149,80 +146,79 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals 0.3.0", + "bitcoin-internals", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.13.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ - "bitcoin-internals 0.2.0", - "hex-conservative 0.1.2", + "bitcoin-io", + "hex-conservative", + "serde", ] [[package]] -name = "bitcoin_hashes" -version = "0.14.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" -dependencies = [ - "bitcoin-io", - "hex-conservative 0.2.1", - "serde", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "1.0.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.3.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.25" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" - -[[package]] -name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.6" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "num-integer", + "iana-time-zone", + "js-sys", "num-traits", "serde", - "time", + "wasm-bindgen", + "windows-link", ] [[package]] @@ -233,7 +229,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim", "textwrap", "unicode-width", @@ -241,19 +237,10 @@ dependencies = [ ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "dtoa" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "elements" @@ -261,7 +248,7 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81b2569d3495bfdfce36c504fd4d78752ff4a7699f8a33e6f3ee523bddf9f6ad" dependencies = [ - "bech32 0.11.0", + "bech32", "bitcoin", "secp256k1-zkp", "serde", @@ -270,37 +257,65 @@ dependencies = [ [[package]] name = "fern" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b48af88aaf938b11baef948a5599e66e709cf92854aa2b87c71f1bcf20f80a01" +checksum = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" dependencies = [ + "chrono", "log", ] [[package]] -name = "fuchsia-cprng" -version = "0.1.1" +name = "find-msvc-tools" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "hal" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d794c6b7443e77bb78df235de0a7f08913a77427b3244ddedef442e9a1cb442" +checksum = "f57a34f3cc18f2967a98c61abea03b1b9521075e880b18f45b63561a88c3c174" dependencies = [ "base64-compat", "bip39", @@ -331,30 +346,43 @@ dependencies = [ "fern", "hal", "hex", + "hyper", + "hyper-util", "log", "serde", "serde_json", "serde_yaml", "simplicity-lang", + "thiserror", + "tokio", ] [[package]] -name = "hex" -version = "0.3.2" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "hex-conservative" -version = "0.1.2" +name = "hermit-abi" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" [[package]] name = "hex-conservative" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ "arrayvec", ] @@ -365,28 +393,129 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "itoa" -version = "0.4.3" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.12" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd80e58f77e0cdea53ba96acc5e04479e5ffc5d869626a6beafe50fed867eace" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.4", "libc", - "log", - "rand 0.6.5", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -400,30 +529,28 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "lightning-invoice" -version = "0.32.0" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ab9f6ea77e20e3129235e62a2e6bd64ed932363df104e864ee65ccffb54a8f" +checksum = "11209f386879b97198b2bfc9e9c1e5d42870825c6bd4376f17f95357244d6600" dependencies = [ - "bech32 0.9.1", + "bech32", "bitcoin", "lightning-types", ] [[package]] name = "lightning-types" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1083b8d9137000edf3bfcb1ff011c0d25e0cdd2feb98cc21d6765e64a494148f" +checksum = "f2cd84d4e71472035903e43caded8ecc123066ce466329ccd5ae537a8d5488c7" dependencies = [ - "bech32 0.9.1", "bitcoin", - "hex-conservative 0.2.1", ] [[package]] @@ -433,46 +560,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] -name = "log" -version = "0.4.6" +name = "lock_api" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "cfg-if 0.1.7", + "scopeguard", ] +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "miniscript" -version = "12.3.2" +version = "12.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0760e92feaf4ee26bd2e616f557de64712bf1e75f3b1b218dfb475c0a84c7943" +checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" dependencies = [ - "bech32 0.11.0", + "bech32", "bitcoin", ] [[package]] -name = "num-integer" -version = "0.1.39" +name = "mio" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ - "num-traits", + "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -482,194 +617,117 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.60" +name = "parking_lot" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ - "unicode-ident", + "lock_api", + "parking_lot_core", ] [[package]] -name = "quote" -version = "1.0.26" +name = "parking_lot_core" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.2", + "cfg-if", "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.0", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.2", - "rand_core 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "redox_syscall", + "smallvec", + "windows-link", ] [[package]] -name = "rand_core" -version = "0.3.1" +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.0", -] +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] -name = "rand_core" -version = "0.4.0" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "rand_core" -version = "0.6.4" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "getrandom", + "zerocopy", ] [[package]] -name = "rand_hc" -version = "0.1.0" +name = "proc-macro2" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ - "rand_core 0.3.1", + "unicode-ident", ] [[package]] -name = "rand_isaac" -version = "0.1.1" +name = "quote" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ - "rand_core 0.3.1", + "proc-macro2", ] [[package]] -name = "rand_jitter" -version = "0.1.3" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" -dependencies = [ - "libc", - "rand_core 0.4.0", - "winapi", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand_os" -version = "0.1.3" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "cloudabi", - "fuchsia-cprng", "libc", - "rand_core 0.4.0", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.2", - "rand_core 0.4.0", + "rand_chacha", + "rand_core", ] [[package]] -name = "rand_xorshift" -version = "0.1.1" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "rand_core 0.3.1", + "ppv-lite86", + "rand_core", ] [[package]] -name = "rdrand" -version = "0.4.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.3.1", + "getrandom 0.2.16", ] [[package]] name = "redox_syscall" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" - -[[package]] -name = "redox_termios" -version = "0.1.1" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "redox_syscall", + "bitflags 2.10.0", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -679,9 +737,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -690,15 +748,21 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "0.2.7" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "santiago" @@ -709,14 +773,20 @@ dependencies = [ "regex", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "secp256k1" version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes 0.14.0", - "rand 0.8.5", + "bitcoin_hashes", + "rand", "secp256k1-sys", "serde", ] @@ -737,7 +807,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" dependencies = [ "bitcoin-private", - "rand 0.8.5", + "rand", "secp256k1", "secp256k1-zkp-sys", "serde", @@ -755,52 +825,64 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.113" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6135c78461981c79497158ef777264c51d9d0f4f3fc3a4d22b915900e42dac6a" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.113" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c5eaa17d0954cb481cdcfffe9d84fcfa7a1a9f2349271e678677be4c26ae31" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "serde_json" -version = "1.0.39" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", + "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_yaml" -version = "0.8.8" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0887a8e097a69559b56aa2526bf7aff7c3048cf627dff781f0b56a6001534593" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ - "dtoa", - "linked-hash-map", + "indexmap", + "ryu", "serde", "yaml-rust", ] [[package]] name = "shell-escape" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "shlex" @@ -808,6 +890,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + [[package]] name = "simplicity-lang" version = "0.5.0" @@ -815,11 +906,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525879699aba1f7f75c0d97355475072adeb0ed0530df4e18f23235252475e68" dependencies = [ "bitcoin", - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "byteorder", "elements", - "getrandom", - "hex-conservative 0.2.1", + "getrandom 0.2.16", + "hex-conservative", "miniscript", "santiago", "serde", @@ -832,32 +923,37 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3abf9c7d64c5bf45bb2fb966f3b0637d8c13c8d5cdfbd7587900421cb7584c49" dependencies = [ - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "cc", ] [[package]] -name = "strsim" -version = "0.8.0" +name = "smallvec" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "syn" -version = "1.0.109" +name = "socket2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "libc", + "windows-sys 0.60.2", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "syn" -version = "2.0.16" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -865,41 +961,39 @@ dependencies = [ ] [[package]] -name = "termion" -version = "1.5.1" +name = "textwrap" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "libc", - "redox_syscall", - "redox_termios", + "unicode-width", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "thiserror" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "unicode-width", + "thiserror-impl", ] [[package]] -name = "time" -version = "0.1.42" +name = "thiserror-impl" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "libc", - "redox_syscall", - "winapi", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -910,69 +1004,94 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.5" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasm-bindgen" -version = "0.2.100" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "cfg-if 1.0.0", - "once_cell", - "wasm-bindgen-macro", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" +name = "wasm-bindgen" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.16", + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -980,31 +1099,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.16", - "wasm-bindgen-backend", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "winapi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -1022,11 +1141,179 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "yaml-rust" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index aab1f76..637972c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,10 @@ hex = "0.3.2" elements = { version = "0.25.2", features = [ "serde", "base64" ] } simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } +hyper = { version = "1.8.1", features = ["server", "http1"] } +hyper-util = { version = "0.1", features = ["tokio"] } +tokio = { version = "1.48.0", features = ["full"] } +thiserror = "2.0.17" [lints.clippy] # Exclude lints we don't think are valuable. diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs new file mode 100644 index 0000000..352a39d --- /dev/null +++ b/src/daemon/mod.rs @@ -0,0 +1,86 @@ +use std::net::SocketAddr; + +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{body::Incoming, Request, Response}; +use hyper_util::rt::TokioIo; +use tokio::net::TcpListener; +use tokio::sync::broadcast; + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum DaemonError { + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + #[error("Address parse error: {0}")] + AddrParse(#[from] std::net::AddrParseError), +} + +pub struct HalSimplicityDaemon { + address: SocketAddr, + shutdown_tx: broadcast::Sender<()>, +} + +impl HalSimplicityDaemon { + pub fn new(address: &str) -> Result { + let address: SocketAddr = address.parse()?; + let (shutdown_tx, _) = broadcast::channel(1); + + Ok(Self { + address, + shutdown_tx, + }) + } + + pub fn start(&mut self) -> Result<(), DaemonError> { + let address = self.address.clone(); + let shutdown_tx = self.shutdown_tx.clone(); + + let runtime = tokio::runtime::Runtime::new()?; + + let listener = runtime.block_on(async { TcpListener::bind(address).await })?; + + std::thread::spawn(move || { + runtime.block_on(async move { + println!("Listening on http://{}", address); + + let mut shutdown_rx = shutdown_tx.subscribe(); + + loop { + tokio::select! { + Ok((stream, _)) = listener.accept() => { + let io = TokioIo::new(stream); + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .serve_connection(io, service_fn(handle_request)) + .await + { + eprintln!("Connection error: {:?}", err); + } + }); + } + _ = shutdown_rx.recv() => { + println!("Server shutting down..."); + break; + } + } + } + }); + }); + + Ok(()) + } + + pub fn shutdown(&self) { + let _ = self.shutdown_tx.send(()); + } +} +async fn handle_request(req: Request) -> Result, DaemonError> { + let path = req.uri().path(); + let method = req.method(); + + println!("Received {} request for {}", method, path); + + Ok(Response::new("Acknowledged".to_string())) +} diff --git a/src/lib.rs b/src/lib.rs index f021541..01aa8d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ pub extern crate simplicity; +#[allow(unused)] +mod daemon; + pub mod address; pub mod block; pub mod hal_simplicity; From 7e7234e13fae877aa5bcfa25f7e4d009db969502 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Tue, 16 Dec 2025 16:10:43 +0200 Subject: [PATCH 02/39] Add simple JSONRPC implementation --- Cargo.lock | 14 ++ Cargo.toml | 1 + src/daemon/handler.rs | 24 +++ src/daemon/mod.rs | 90 +++++++++- src/jsonrpc/mod.rs | 373 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- 6 files changed, 495 insertions(+), 11 deletions(-) create mode 100644 src/daemon/handler.rs create mode 100644 src/jsonrpc/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 62f0da1..8c17826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,7 @@ dependencies = [ "fern", "hal", "hex", + "http-body-util", "hyper", "hyper-util", "log", @@ -413,6 +414,19 @@ dependencies = [ "http", ] +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" diff --git a/Cargo.toml b/Cargo.toml index 637972c..0814ae1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ elements = { version = "0.25.2", features = [ "serde", "base64" ] } simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } hyper = { version = "1.8.1", features = ["server", "http1"] } hyper-util = { version = "0.1", features = ["tokio"] } +http-body-util = "0.1" tokio = { version = "1.48.0", features = ["full"] } thiserror = "2.0.17" diff --git a/src/daemon/handler.rs b/src/daemon/handler.rs new file mode 100644 index 0000000..14a0b95 --- /dev/null +++ b/src/daemon/handler.rs @@ -0,0 +1,24 @@ +use crate::jsonrpc::{ErrorCode, JsonRpcService, RpcError, RpcHandler}; +use serde_json::Value; + +/// Default RPC handler that provides basic methods +pub struct DefaultRpcHandler; + +impl RpcHandler for DefaultRpcHandler { + fn handle(&self, method: &str, _params: Option) -> Result { + match method { + _ => Err(RpcError::new(ErrorCode::MethodNotFound)), + } + } +} + +impl DefaultRpcHandler { + pub fn new() -> Self { + Self + } +} + +/// Create a JSONRPC service with the default handler +pub fn create_service() -> JsonRpcService { + JsonRpcService::new(DefaultRpcHandler::new()) +} diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index 352a39d..0a4a4ff 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -1,14 +1,23 @@ +mod handler; + use std::net::SocketAddr; +use std::sync::Arc; +use http_body_util::{BodyExt, Full}; +use hyper::body::Bytes; use hyper::server::conn::http1; use hyper::service::service_fn; -use hyper::{body::Incoming, Request, Response}; +use hyper::{body::Incoming, Method, Request, Response, StatusCode}; use hyper_util::rt::TokioIo; use tokio::net::TcpListener; use tokio::sync::broadcast; use thiserror::Error; +use crate::jsonrpc::JsonRpcService; +use handler::DefaultRpcHandler; + +/// Errors that can occur in the daemon, usually on startup. #[derive(Error, Debug)] pub enum DaemonError { #[error("IO error: {0}")] @@ -17,43 +26,54 @@ pub enum DaemonError { AddrParse(#[from] std::net::AddrParseError), } +/// The HAL Simplicity Daemon +/// +/// It listens for JSON-RPC requests over HTTP and handles them. +/// Does not block the current thread when started. Instead, it spawns a new thread. pub struct HalSimplicityDaemon { address: SocketAddr, shutdown_tx: broadcast::Sender<()>, + rpc_service: Arc>, } impl HalSimplicityDaemon { pub fn new(address: &str) -> Result { let address: SocketAddr = address.parse()?; let (shutdown_tx, _) = broadcast::channel(1); + let rpc_service = Arc::new(handler::create_service()); Ok(Self { address, shutdown_tx, + rpc_service, }) } + /// Start the daemon on a new thread pub fn start(&mut self) -> Result<(), DaemonError> { - let address = self.address.clone(); + println!("Listening on http://{}", self.address); + let shutdown_tx = self.shutdown_tx.clone(); + let rpc_service = self.rpc_service.clone(); let runtime = tokio::runtime::Runtime::new()?; - let listener = runtime.block_on(async { TcpListener::bind(address).await })?; + let listener = runtime.block_on(async { TcpListener::bind(&self.address).await })?; std::thread::spawn(move || { runtime.block_on(async move { - println!("Listening on http://{}", address); - let mut shutdown_rx = shutdown_tx.subscribe(); loop { tokio::select! { Ok((stream, _)) = listener.accept() => { let io = TokioIo::new(stream); + let rpc_service_clone = rpc_service.clone(); tokio::task::spawn(async move { if let Err(err) = http1::Builder::new() - .serve_connection(io, service_fn(handle_request)) + .serve_connection(io, service_fn(move |req| { + handle_request(req, rpc_service_clone.clone()) + })) .await { eprintln!("Connection error: {:?}", err); @@ -72,15 +92,67 @@ impl HalSimplicityDaemon { Ok(()) } + /// Shutdown the daemon pub fn shutdown(&self) { let _ = self.shutdown_tx.send(()); } } -async fn handle_request(req: Request) -> Result, DaemonError> { + +/// Handles an incoming HTTP request and produces a response. +async fn handle_request( + req: Request, + rpc_service: Arc>, +) -> Result>, DaemonError> { let path = req.uri().path(); let method = req.method(); - println!("Received {} request for {}", method, path); + if method != Method::POST { + return Ok(create_status_response(StatusCode::METHOD_NOT_ALLOWED)); + } + + if path != "/rpc" && path != "/" { + return Ok(create_status_response(StatusCode::NOT_FOUND)); + } + + let body_str = match read_body_as_string(req).await { + Ok(body) => body, + Err(status) => return Ok(create_status_response(status)), + }; + + let response_str = rpc_service.handle_raw(&body_str); + + if response_str.is_empty() { + return Ok(create_status_response(StatusCode::NO_CONTENT)); + } + + Ok(create_json_response(response_str)) +} + +/// Creates an HTTP response with the given status code +fn create_status_response(status: StatusCode) -> Response> { + let body = if status == StatusCode::NO_CONTENT { + Bytes::new() + } else { + Bytes::from(status.canonical_reason().unwrap_or("Unknown Error")) + }; + let mut response = Response::new(Full::new(body)); + *response.status_mut() = status; + response +} + +/// Reads and validates the request body as a UTF-8 string +async fn read_body_as_string(req: Request) -> Result { + let body_bytes = req.collect().await.map_err(|_| StatusCode::BAD_REQUEST)?.to_bytes(); + + String::from_utf8(body_bytes.to_vec()).map_err(|_| StatusCode::BAD_REQUEST) +} - Ok(Response::new("Acknowledged".to_string())) +/// Creates a successful JSON-RPC response +fn create_json_response(body: String) -> Response> { + let mut response = Response::new(Full::new(Bytes::from(body))); + response.headers_mut().insert( + hyper::header::CONTENT_TYPE, + hyper::header::HeaderValue::from_static("application/json"), + ); + response } diff --git a/src/jsonrpc/mod.rs b/src/jsonrpc/mod.rs new file mode 100644 index 0000000..996ce81 --- /dev/null +++ b/src/jsonrpc/mod.rs @@ -0,0 +1,373 @@ +//! Simple JSONRPC 2.0 implementation +//! +//! https://www.jsonrpc.org/specification + +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fmt; + +/// JSONRPC 2.0 Error codes as defined in the specification +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ErrorCode { + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, +} + +impl ErrorCode { + pub fn code(&self) -> i64 { + *self as i64 + } + + pub fn message(&self) -> &str { + match self { + ErrorCode::ParseError => "Parse error", + ErrorCode::InvalidRequest => "Invalid Request", + ErrorCode::MethodNotFound => "Method not found", + ErrorCode::InvalidParams => "Invalid params", + ErrorCode::InternalError => "Internal error", + } + } +} + +/// JSONRPC 2.0 Error object +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RpcError { + pub code: i64, + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, +} + +impl RpcError { + pub fn new(error_code: ErrorCode) -> Self { + Self { + code: error_code.code(), + message: error_code.message().to_string(), + data: None, + } + } + + pub fn with_data(mut self, data: Value) -> Self { + self.data = Some(data); + self + } + + pub fn custom(code: i64, message: String) -> Self { + Self { + code, + message, + data: None, + } + } +} + +impl fmt::Display for RpcError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RPC Error {}: {}", self.code, self.message) + } +} + +impl std::error::Error for RpcError {} + +/// JSONRPC 2.0 Request object +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RpcRequest { + pub jsonrpc: String, + pub method: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub params: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +impl RpcRequest { + pub fn new(method: String, params: Option, id: Option) -> Self { + Self { + jsonrpc: "2.0".to_string(), + method, + params, + id, + } + } + + /// Check if this is a notification (no id field) + pub fn is_notification(&self) -> bool { + self.id.is_none() + } + + /// Validate the request according to JSONRPC 2.0 spec + pub fn validate(&self) -> Result<(), RpcError> { + if self.jsonrpc != "2.0" { + return Err(RpcError::new(ErrorCode::InvalidRequest) + .with_data(Value::String("jsonrpc field must be '2.0'".to_string()))); + } + + if self.method.is_empty() { + return Err(RpcError::new(ErrorCode::InvalidRequest) + .with_data(Value::String("method field cannot be empty".to_string()))); + } + + Ok(()) + } +} + +/// JSONRPC 2.0 Response object +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RpcResponse { + pub jsonrpc: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub result: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + pub id: Value, +} + +impl RpcResponse { + pub fn success(result: Value, id: Value) -> Self { + Self { + jsonrpc: "2.0".to_string(), + result: Some(result), + error: None, + id, + } + } + + pub fn error(error: RpcError, id: Value) -> Self { + Self { + jsonrpc: "2.0".to_string(), + result: None, + error: Some(error), + id, + } + } +} + +/// Represents either a single request or batch requests +#[derive(Debug)] +pub enum RpcCall { + Single(RpcRequest), + Batch(Vec), +} + +impl RpcCall { + /// Parse a JSON string into an RPC call + pub fn from_json(json: &str) -> Result { + // Try parsing as a single request first + if let Ok(request) = serde_json::from_str::(json) { + request.validate()?; + return Ok(RpcCall::Single(request)); + } + + // Try psrsing as a batch request + match serde_json::from_str::>(json) { + Ok(requests) => { + if requests.is_empty() { + return Err(RpcError::new(ErrorCode::InvalidRequest) + .with_data(Value::String("batch request cannot be empty".to_string()))); + } + + for request in &requests { + request.validate()?; + } + + Ok(RpcCall::Batch(requests)) + } + Err(_) => Err(RpcError::new(ErrorCode::ParseError)), + } + } +} + +/// Represents either a single response or batch responses +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum RpcOutput { + Single(RpcResponse), + Batch(Vec), +} + +impl RpcOutput { + pub fn to_json(&self) -> Result { + serde_json::to_string(self) + } +} + +/// Handler trait for RPC methods +pub trait RpcHandler: Send + Sync { + fn handle(&self, method: &str, params: Option) -> Result; +} + +/// Main JSONRPC service +pub struct JsonRpcService { + handler: H, +} + +impl JsonRpcService { + pub fn new(handler: H) -> Self { + Self { + handler, + } + } + + /// Process a raw JSON string and return a JSON response + pub fn handle_raw(&self, json: &str) -> String { + match RpcCall::from_json(json) { + Ok(call) => match call { + RpcCall::Single(request) => { + let response = self.handle_single(request); + if let Some(resp) = response { + serde_json::to_string(&resp).unwrap_or_else(|_| { + serde_json::to_string(&RpcResponse::error( + RpcError::new(ErrorCode::InternalError), + Value::Null, + )) + .unwrap() + }) + } else { + // Notification - no response + String::new() + } + } + RpcCall::Batch(requests) => { + let responses = self.handle_batch(requests); + if responses.is_empty() { + // All notifications - no response + String::new() + } else { + RpcOutput::Batch(responses).to_json().unwrap_or_else(|_| { + serde_json::to_string(&RpcResponse::error( + RpcError::new(ErrorCode::InternalError), + Value::Null, + )) + .unwrap() + }) + } + } + }, + Err(error) => { + serde_json::to_string(&RpcResponse::error(error, Value::Null)).expect("should ") + } + } + } + + /// Handle a single RPC request + fn handle_single(&self, request: RpcRequest) -> Option { + // Notifications don't get responses + if request.is_notification() { + let _ = self.handler.handle(&request.method, request.params); + return None; + } + + let id = request.id.clone().unwrap_or(Value::Null); + + let response = match self.handler.handle(&request.method, request.params) { + Ok(result) => RpcResponse::success(result, id), + Err(error) => RpcResponse::error(error, id), + }; + + Some(response) + } + + /// Handle a batch of RPC requests + fn handle_batch(&self, requests: Vec) -> Vec { + requests.into_iter().filter_map(|request| self.handle_single(request)).collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct TestHandler; + + impl RpcHandler for TestHandler { + fn handle(&self, method: &str, params: Option) -> Result { + match method { + "echo" => Ok(params.unwrap_or(Value::Null)), + "add" => { + let params = params.ok_or_else(|| RpcError::new(ErrorCode::InvalidParams))?; + let array = + params.as_array().ok_or_else(|| RpcError::new(ErrorCode::InvalidParams))?; + if array.len() != 2 { + return Err(RpcError::new(ErrorCode::InvalidParams)); + } + let a = + array[0].as_i64().ok_or_else(|| RpcError::new(ErrorCode::InvalidParams))?; + let b = + array[1].as_i64().ok_or_else(|| RpcError::new(ErrorCode::InvalidParams))?; + Ok(Value::Number((a + b).into())) + } + _ => Err(RpcError::new(ErrorCode::MethodNotFound)), + } + } + } + + #[test] + fn test_single_request() { + let service = JsonRpcService::new(TestHandler); + let request = r#"{"jsonrpc":"2.0","method":"echo","params":"hello","id":1}"#; + let response = service.handle_raw(request); + assert!(response.contains(r#""result":"hello""#)); + assert!(response.contains(r#""id":1"#)); + } + + #[test] + fn test_notification() { + let service = JsonRpcService::new(TestHandler); + let request = r#"{"jsonrpc":"2.0","method":"echo","params":"hello"}"#; + let response = service.handle_raw(request); + assert_eq!(response, ""); + } + + #[test] + fn test_batch_request() { + let service = JsonRpcService::new(TestHandler); + let request = r#"[ + {"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}, + {"jsonrpc":"2.0","method":"add","params":[3,4],"id":2} + ]"#; + let response = service.handle_raw(request); + assert!(response.contains(r#""result":3"#)); + assert!(response.contains(r#""result":7"#)); + } + + #[test] + fn test_method_not_found() { + let service = JsonRpcService::new(TestHandler); + let request = r#"{"jsonrpc":"2.0","method":"unknown","id":1}"#; + let response = service.handle_raw(request); + assert!(response.contains(r#""code":-32601"#)); + assert!(response.contains("Method not found")); + } + + #[test] + fn test_invalid_json() { + let service = JsonRpcService::new(TestHandler); + let request = r#"{"jsonrpc":"2.0","method":"#; + let response = service.handle_raw(request); + assert!(response.contains(r#""code":-32700"#)); + } + + #[test] + fn test_invalid_request() { + let service = JsonRpcService::new(TestHandler); + let request = r#"{"jsonrpc":"1.0","method":"echo","id":1}"#; + let response = service.handle_raw(request); + assert!(response.contains(r#""code":-32600"#)); + } + + #[test] + fn test_batch_with_notifications() { + let service = JsonRpcService::new(TestHandler); + let request = r#"[ + {"jsonrpc":"2.0","method":"echo","params":"notify"}, + {"jsonrpc":"2.0","method":"add","params":[1,2],"id":1} + ]"#; + let response = service.handle_raw(request); + // Should only have one response (the non-notification) + assert!(response.contains(r#""result":3"#)); + assert!(response.contains(r#""id":1"#)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 01aa8d8..b39cdcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ pub extern crate simplicity; -#[allow(unused)] -mod daemon; +pub mod daemon; +pub mod jsonrpc; pub mod address; pub mod block; From c9cc91002ceed989c650d4fca9e34303548ded22 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 13:33:29 +0200 Subject: [PATCH 03/39] Move CLI commands as actions for the handler and add the error handling --- src/daemon/actions/address.rs | 134 +++++ src/daemon/actions/block.rs | 221 ++++++++ src/daemon/actions/keypair.rs | 14 + src/daemon/actions/mod.rs | 9 + src/daemon/actions/simplicity/info.rs | 60 ++ src/daemon/actions/simplicity/mod.rs | 7 + src/daemon/actions/simplicity/pset/create.rs | 152 +++++ src/daemon/actions/simplicity/pset/extract.rs | 27 + .../actions/simplicity/pset/finalize.rs | 56 ++ src/daemon/actions/simplicity/pset/mod.rs | 112 ++++ src/daemon/actions/simplicity/pset/run.rs | 109 ++++ .../actions/simplicity/pset/update_input.rs | 191 +++++++ src/daemon/actions/simplicity/sighash.rs | 281 +++++++++ src/daemon/actions/tx.rs | 532 ++++++++++++++++++ src/daemon/actions/types.rs | 190 +++++++ src/daemon/mod.rs | 1 + 16 files changed, 2096 insertions(+) create mode 100644 src/daemon/actions/address.rs create mode 100644 src/daemon/actions/block.rs create mode 100644 src/daemon/actions/keypair.rs create mode 100644 src/daemon/actions/mod.rs create mode 100644 src/daemon/actions/simplicity/info.rs create mode 100644 src/daemon/actions/simplicity/mod.rs create mode 100644 src/daemon/actions/simplicity/pset/create.rs create mode 100644 src/daemon/actions/simplicity/pset/extract.rs create mode 100644 src/daemon/actions/simplicity/pset/finalize.rs create mode 100644 src/daemon/actions/simplicity/pset/mod.rs create mode 100644 src/daemon/actions/simplicity/pset/run.rs create mode 100644 src/daemon/actions/simplicity/pset/update_input.rs create mode 100644 src/daemon/actions/simplicity/sighash.rs create mode 100644 src/daemon/actions/tx.rs create mode 100644 src/daemon/actions/types.rs diff --git a/src/daemon/actions/address.rs b/src/daemon/actions/address.rs new file mode 100644 index 0000000..36cd0f6 --- /dev/null +++ b/src/daemon/actions/address.rs @@ -0,0 +1,134 @@ +use elements::bitcoin::{secp256k1, PublicKey}; +use elements::hashes::Hash; +use elements::{Address, WPubkeyHash, WScriptHash}; +use thiserror::Error; + +use crate::{address::AddressInfo, Network}; + +use super::types::AddressCreateRequest; + +#[derive(Debug, Error)] +pub enum AddressError { + #[error("Failed to parse network: {0}")] + NetworkParse(String), + + #[error("Failed to parse blinder hex: {0}")] + BlinderHex(hex::FromHexError), + + #[error("Invalid blinder: {0}")] + BlinderInvalid(secp256k1::Error), + + #[error("Invalid pubkey: {0}")] + PubkeyInvalid(elements::bitcoin::key::ParsePublicKeyError), + + #[error("Invalid script hex: {0}")] + ScriptHex(hex::FromHexError), + + #[error("Either pubkey or script must be provided")] + MissingInput, + + #[error("Invalid address format: {0}")] + AddressParse(elements::address::AddressError), +} + +pub fn create(req: AddressCreateRequest) -> Result { + let network = + req.network.as_deref().map(parse_network).transpose()?.unwrap_or(Network::ElementsRegtest); + + let blinder = req + .blinder + .map(|b| { + let bytes = hex::decode(&b).map_err(AddressError::BlinderHex)?; + secp256k1::PublicKey::from_slice(&bytes).map_err(AddressError::BlinderInvalid) + }) + .transpose()?; + + if let Some(pubkey_hex) = req.pubkey { + let pubkey: PublicKey = pubkey_hex.parse().map_err(AddressError::PubkeyInvalid)?; + Ok(crate::address::Addresses::from_pubkey(&pubkey, blinder, network)) + } else if let Some(script_hex) = req.script { + let script_bytes = hex::decode(&script_hex).map_err(AddressError::ScriptHex)?; + let script = script_bytes.into(); + Ok(crate::address::Addresses::from_script(&script, blinder, network)) + } else { + Err(AddressError::MissingInput) + } +} + +pub fn inspect(address_str: &str) -> Result { + let address: Address = address_str.parse().map_err(AddressError::AddressParse)?; + + let script_pk = address.script_pubkey(); + + let mut info = AddressInfo { + network: Network::from_params(address.params).expect("addresses always have params"), + script_pub_key: hal::tx::OutputScriptInfo { + hex: Some(script_pk.to_bytes().into()), + asm: Some(script_pk.asm()), + address: None, + type_: None, + }, + type_: None, + pubkey_hash: None, + script_hash: None, + witness_pubkey_hash: None, + witness_script_hash: None, + witness_program_version: None, + blinding_pubkey: address.blinding_pubkey, + unconfidential: if address.blinding_pubkey.is_some() { + Some(Address { + params: address.params, + payload: address.payload.clone(), + blinding_pubkey: None, + }) + } else { + None + }, + }; + + use elements::address::Payload; + match address.payload { + Payload::PubkeyHash(pkh) => { + info.type_ = Some("p2pkh".to_owned()); + info.pubkey_hash = Some(pkh); + } + Payload::ScriptHash(sh) => { + info.type_ = Some("p2sh".to_owned()); + info.script_hash = Some(sh); + } + Payload::WitnessProgram { + version, + program, + } => { + let version = version.to_u8() as usize; + info.witness_program_version = Some(version); + + if version == 0 { + if program.len() == 20 { + info.type_ = Some("p2wpkh".to_owned()); + info.witness_pubkey_hash = + Some(WPubkeyHash::from_slice(&program).expect("size 20")); + } else if program.len() == 32 { + info.type_ = Some("p2wsh".to_owned()); + info.witness_script_hash = + Some(WScriptHash::from_slice(&program).expect("size 32")); + } else { + info.type_ = Some("invalid-witness-program".to_owned()); + } + } else { + info.type_ = Some("unknown-witness-program-version".to_owned()); + } + } + } + + Ok(info) +} + +fn parse_network(s: &str) -> Result { + match s.to_lowercase().as_str() { + "liquid" => Ok(Network::Liquid), + "liquid-testnet" | "liquidtestnet" => Ok(Network::LiquidTestnet), + "elementsregtest" | "elements-regtest" | "regtest" => Ok(Network::ElementsRegtest), + _ => Err(AddressError::NetworkParse(format!("unknown network: {}", s))), + } +} diff --git a/src/daemon/actions/block.rs b/src/daemon/actions/block.rs new file mode 100644 index 0000000..b4ed95e --- /dev/null +++ b/src/daemon/actions/block.rs @@ -0,0 +1,221 @@ +use elements::encode::{deserialize, serialize}; +use elements::{dynafed, Block, BlockExtData, BlockHeader}; +use thiserror::Error; + +use crate::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; +use crate::Network; + +use super::types::{BlockCreateRequest, BlockDecodeRequest}; + +#[derive(Debug, Error)] +pub enum BlockError { + #[error("Failed to parse block info JSON: {0}")] + JsonParse(serde_json::Error), + #[error("Cannot provide transactions both in JSON and raw")] + ConflictingTransactions, + + #[error("No transactions provided")] + NoTransactions, + + #[error("Failed to deserialize raw transaction: {0}")] + TransactionDeserialize(super::tx::TxError), + + #[error("Failed to deserialize raw transaction using Elements: {0}")] + ElementsTransactionDeserialize(elements::encode::Error), + + #[error("Failed to decode raw block hex: {0}")] + BlockHex(hex::FromHexError), + + #[error("Invalid block format: {0}")] + BlockDeserialize(elements::encode::Error), + + #[error("Failed to serialize response: {0}")] + Serialize(String), + + #[error("Failed to parse network: {0}")] + NetworkParse(String), + + #[error("Missing {field} in {context}")] + MissingField { + field: String, + context: String, + }, +} + +pub fn create(req: BlockCreateRequest) -> Result { + let info = serde_json::from_str::(&req.block_info).map_err(BlockError::JsonParse)?; + + let block = Block { + header: create_block_header(info.header)?, + txdata: match (info.transactions, info.raw_transactions) { + (Some(_), Some(_)) => return Err(BlockError::ConflictingTransactions), + (None, None) => return Err(BlockError::NoTransactions), + (Some(infos), None) => infos + .into_iter() + .map(|info| { + super::tx::create_transaction(info).map_err(BlockError::TransactionDeserialize) + }) + .collect::, _>>()?, + (None, Some(raws)) => raws + .into_iter() + .map(|r| deserialize(&r.0).map_err(BlockError::ElementsTransactionDeserialize)) + .collect::, _>>()?, + }, + }; + + let block_bytes = serialize(&block); + Ok(hex::encode(&block_bytes)) +} + +pub fn decode(req: BlockDecodeRequest) -> Result { + let raw_block = hex::decode(&req.raw_block).map_err(BlockError::BlockHex)?; + let network = req + .network + .as_ref() + .map(|s| parse_network(s)) + .transpose()? + .unwrap_or(Network::ElementsRegtest); + if req.txids.unwrap_or(false) { + let block: Block = deserialize(&raw_block).map_err(BlockError::BlockDeserialize)?; + let info = BlockInfo { + header: crate::GetInfo::get_info(&block.header, network), + txids: Some(block.txdata.iter().map(|t| t.txid()).collect()), + transactions: None, + raw_transactions: None, + }; + serde_json::to_value(&info).map_err(|e| BlockError::Serialize(format!("{}", e))) + } else { + let header: BlockHeader = match deserialize(&raw_block) { + Ok(header) => header, + Err(_) => { + let block: Block = deserialize(&raw_block).map_err(BlockError::BlockDeserialize)?; + block.header + } + }; + let info = crate::GetInfo::get_info(&header, network); + serde_json::to_value(&info).map_err(|e| BlockError::Serialize(format!("{}", e))) + } +} + +fn create_params(info: ParamsInfo) -> Result { + Ok(match info.params_type { + ParamsType::Null => dynafed::Params::Null, + ParamsType::Compact => dynafed::Params::Compact { + signblockscript: info + .signblockscript + .ok_or_else(|| BlockError::MissingField { + field: "signblockscript".to_string(), + context: "compact params".to_string(), + })? + .0 + .into(), + signblock_witness_limit: info.signblock_witness_limit.ok_or_else(|| { + BlockError::MissingField { + field: "signblock_witness_limit".to_string(), + context: "compact params".to_string(), + } + })?, + elided_root: info.elided_root.ok_or_else(|| BlockError::MissingField { + field: "elided_root".to_string(), + context: "compact params".to_string(), + })?, + }, + ParamsType::Full => dynafed::Params::Full(dynafed::FullParams::new( + info.signblockscript + .ok_or_else(|| BlockError::MissingField { + field: "signblockscript".to_string(), + context: "full params".to_string(), + })? + .0 + .into(), + info.signblock_witness_limit.ok_or_else(|| BlockError::MissingField { + field: "signblock_witness_limit".to_string(), + context: "full params".to_string(), + })?, + info.fedpeg_program + .ok_or_else(|| BlockError::MissingField { + field: "fedpeg_program".to_string(), + context: "full params".to_string(), + })? + .0 + .into(), + info.fedpeg_script + .ok_or_else(|| BlockError::MissingField { + field: "fedpeg_script".to_string(), + context: "full params".to_string(), + })? + .0, + info.extension_space + .ok_or_else(|| BlockError::MissingField { + field: "extension_space".to_string(), + context: "full params".to_string(), + })? + .into_iter() + .map(|b| b.0) + .collect(), + )), + }) +} + +fn create_block_header(info: BlockHeaderInfo) -> Result { + Ok(BlockHeader { + version: info.version, + prev_blockhash: info.previous_block_hash, + merkle_root: info.merkle_root, + time: info.time, + height: info.height, + ext: if info.dynafed { + BlockExtData::Dynafed { + current: create_params(info.dynafed_current.ok_or_else(|| { + BlockError::MissingField { + field: "dynafed_current".to_string(), + context: "block header".to_string(), + } + })?)?, + proposed: create_params(info.dynafed_proposed.ok_or_else(|| { + BlockError::MissingField { + field: "dynafed_proposed".to_string(), + context: "block header".to_string(), + } + })?)?, + signblock_witness: info + .dynafed_witness + .ok_or_else(|| BlockError::MissingField { + field: "dynafed_witness".to_string(), + context: "block header".to_string(), + })? + .into_iter() + .map(|b| b.0) + .collect(), + } + } else { + BlockExtData::Proof { + challenge: info + .legacy_challenge + .ok_or_else(|| BlockError::MissingField { + field: "legacy_challenge".to_string(), + context: "block header".to_string(), + })? + .0 + .into(), + solution: info + .legacy_solution + .ok_or_else(|| BlockError::MissingField { + field: "legacy_solution".to_string(), + context: "block header".to_string(), + })? + .0 + .into(), + } + }, + }) +} + +fn parse_network(s: &str) -> Result { + match s.to_lowercase().as_str() { + "liquid" => Ok(Network::Liquid), + "liquid-testnet" | "liquidtestnet" => Ok(Network::LiquidTestnet), + "elementsregtest" | "elements-regtest" | "regtest" => Ok(Network::ElementsRegtest), + _ => Err(BlockError::NetworkParse(format!("unknown network: {}", s))), + } +} diff --git a/src/daemon/actions/keypair.rs b/src/daemon/actions/keypair.rs new file mode 100644 index 0000000..ed97410 --- /dev/null +++ b/src/daemon/actions/keypair.rs @@ -0,0 +1,14 @@ +use elements::bitcoin::secp256k1::{self, rand}; + +use super::types::KeypairGenerateResponse; + +pub fn generate() -> KeypairGenerateResponse { + let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng()); + let (x_only, parity) = public.x_only_public_key(); + + KeypairGenerateResponse { + secret, + x_only, + parity, + } +} diff --git a/src/daemon/actions/mod.rs b/src/daemon/actions/mod.rs new file mode 100644 index 0000000..5dc3157 --- /dev/null +++ b/src/daemon/actions/mod.rs @@ -0,0 +1,9 @@ +mod types; + +pub mod address; +pub mod block; +pub mod keypair; +pub mod simplicity; +pub mod tx; + +pub use types::*; diff --git a/src/daemon/actions/simplicity/info.rs b/src/daemon/actions/simplicity/info.rs new file mode 100644 index 0000000..aa043fe --- /dev/null +++ b/src/daemon/actions/simplicity/info.rs @@ -0,0 +1,60 @@ +use crate::daemon::actions::types::{RedeemInfo, SimplicityInfoRequest, SimplicityInfoResponse}; +use crate::hal_simplicity::{elements_address, Program}; +use crate::simplicity::jet; +use simplicity::hex::parse::FromHex as _; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum SimplicityInfoError { + #[error("Failed to parse program: {0}")] + ProgramParse(simplicity::ParseError), + + #[error("Failed to parse state (32-byte hex): {0}")] + StateParse(elements::hashes::hex::HexToArrayError), +} + +pub fn info(req: SimplicityInfoRequest) -> Result { + let program = Program::::from_str(&req.program, req.witness.as_deref()) + .map_err(SimplicityInfoError::ProgramParse)?; + + let redeem_info = program.redeem_node().map(|node| { + let disp = node.display(); + let redeem_base64 = disp.program().to_string(); + let witness_hex = disp.witness().to_string(); + RedeemInfo { + redeem_base64, + witness_hex, + amr: node.amr(), + ihr: node.ihr(), + } + }); + + let state = req + .state + .as_deref() + .map(<[u8; 32]>::from_hex) + .transpose() + .map_err(SimplicityInfoError::StateParse)?; + + Ok(SimplicityInfoResponse { + jets: "core", + commit_base64: program.commit_prog().to_string(), + commit_decode: program.commit_prog().display_expr().to_string(), + type_arrow: program.commit_prog().arrow().to_string(), + cmr: program.cmr(), + liquid_address_unconf: elements_address( + program.cmr(), + state, + &elements::AddressParams::LIQUID, + ) + .to_string(), + liquid_testnet_address_unconf: elements_address( + program.cmr(), + state, + &elements::AddressParams::LIQUID_TESTNET, + ) + .to_string(), + is_redeem: redeem_info.is_some(), + redeem_info, + }) +} diff --git a/src/daemon/actions/simplicity/mod.rs b/src/daemon/actions/simplicity/mod.rs new file mode 100644 index 0000000..567f066 --- /dev/null +++ b/src/daemon/actions/simplicity/mod.rs @@ -0,0 +1,7 @@ +mod info; +mod pset; +mod sighash; + +pub use info::info; +pub use pset::{create, extract, finalize, run, update_input}; +pub use sighash::sighash; diff --git a/src/daemon/actions/simplicity/pset/create.rs b/src/daemon/actions/simplicity/pset/create.rs new file mode 100644 index 0000000..f6cc3b9 --- /dev/null +++ b/src/daemon/actions/simplicity/pset/create.rs @@ -0,0 +1,152 @@ +use super::PsetError; +use crate::daemon::actions::types::{PsetCreateRequest, PsetCreateResponse}; +use elements::confidential; +use elements::pset::PartiallySignedTransaction; +use elements::{Address, AssetId, OutPoint, Transaction, TxIn, TxOut, Txid}; +use serde::Deserialize; +use std::collections::HashMap; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PsetCreateError { + #[error(transparent)] + SharedError(#[from] PsetError), + + #[error("Failed to parse inputs JSON: {0}")] + InputsJsonParse(serde_json::Error), + + #[error("Failed to parse outputs JSON: {0}")] + OutputsJsonParse(serde_json::Error), + + #[error("Failed to parse amount: {0}")] + AmountParse(elements::bitcoin::amount::ParseAmountError), + + #[error("Failed to parse address: {0}")] + AddressParse(elements::address::AddressError), + + #[error("Confidential addresses are not yet supported")] + ConfidentialAddressNotSupported, +} + +#[derive(Deserialize)] +struct InputSpec { + txid: Txid, + vout: u32, + #[serde(default)] + sequence: Option, +} + +#[derive(Deserialize)] +struct FlattenedOutputSpec { + address: String, + asset: AssetId, + #[serde(with = "elements::bitcoin::amount::serde::as_btc")] + amount: elements::bitcoin::Amount, +} + +#[derive(Deserialize)] +#[serde(untagged)] +enum OutputSpec { + Explicit { + address: String, + asset: AssetId, + #[serde(with = "elements::bitcoin::amount::serde::as_btc")] + amount: elements::bitcoin::Amount, + }, + Map(HashMap), +} + +impl OutputSpec { + fn flatten(self) -> Box>> { + match self { + Self::Map(map) => Box::new(map.into_iter().map(|(address, amount)| { + let default_asset = AssetId::from_slice(&[ + 0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3, 0x9f, 0xc0, 0x3b, 0x63, 0x7f, + 0x2a, 0x4e, 0x1e, 0x64, 0xe5, 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f, 0x6d, 0x71, + 0xaa, 0x44, 0x43, 0x65, 0x4c, 0x14, + ]) + .expect("valid asset id"); + + Ok(FlattenedOutputSpec { + address, + asset: default_asset, + amount: elements::bitcoin::Amount::from_btc(amount) + .map_err(PsetCreateError::AmountParse)?, + }) + })), + Self::Explicit { + address, + asset, + amount, + } => Box::new( + Some(Ok(FlattenedOutputSpec { + address, + asset, + amount, + })) + .into_iter(), + ), + } + } +} + +pub fn create(req: PsetCreateRequest) -> Result { + let input_specs: Vec = + serde_json::from_str(&req.inputs).map_err(PsetCreateError::InputsJsonParse)?; + + let output_specs: Vec = + serde_json::from_str(&req.outputs).map_err(PsetCreateError::OutputsJsonParse)?; + + let mut inputs = Vec::new(); + for input_spec in &input_specs { + let outpoint = OutPoint::new(input_spec.txid, input_spec.vout); + let sequence = elements::Sequence(input_spec.sequence.unwrap_or(0xffffffff)); + + inputs.push(TxIn { + previous_output: outpoint, + script_sig: elements::Script::new(), + sequence, + asset_issuance: Default::default(), + witness: Default::default(), + is_pegin: false, + }); + } + + let mut outputs = Vec::new(); + for output_spec in output_specs.into_iter().flat_map(OutputSpec::flatten) { + let output_spec = output_spec?; + + let script_pubkey = match output_spec.address.as_str() { + "fee" => elements::Script::new(), + x => { + let addr = x.parse::
().map_err(PsetCreateError::AddressParse)?; + if addr.is_blinded() { + return Err(PsetCreateError::ConfidentialAddressNotSupported); + } + addr.script_pubkey() + } + }; + + outputs.push(TxOut { + asset: confidential::Asset::Explicit(output_spec.asset), + value: confidential::Value::Explicit(output_spec.amount.to_sat()), + nonce: elements::confidential::Nonce::Null, + script_pubkey, + witness: elements::TxOutWitness::empty(), + }); + } + + let tx = Transaction { + version: 2, + lock_time: elements::LockTime::ZERO, + input: inputs, + output: outputs, + }; + + let pset = PartiallySignedTransaction::from_tx(tx); + + Ok(PsetCreateResponse { + pset: pset.to_string(), + updated_values: vec![], + }) +} diff --git a/src/daemon/actions/simplicity/pset/extract.rs b/src/daemon/actions/simplicity/pset/extract.rs new file mode 100644 index 0000000..a61c1eb --- /dev/null +++ b/src/daemon/actions/simplicity/pset/extract.rs @@ -0,0 +1,27 @@ +use super::PsetError; +use crate::daemon::actions::types::{PsetExtractRequest, PsetExtractResponse}; +use elements::encode::serialize_hex; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PsetExtractError { + #[error(transparent)] + SharedError(#[from] PsetError), + + #[error("Failed to decode PSET: {0}")] + PsetDecode(elements::pset::ParseError), + + #[error("Failed to extract transaction: {0}")] + TransactionExtract(elements::pset::Error), +} + +pub fn extract(req: PsetExtractRequest) -> Result { + let pset: elements::pset::PartiallySignedTransaction = + req.pset.parse().map_err(PsetExtractError::PsetDecode)?; + + let tx = pset.extract_tx().map_err(PsetExtractError::TransactionExtract)?; + + Ok(PsetExtractResponse { + raw_tx: serialize_hex(&tx), + }) +} diff --git a/src/daemon/actions/simplicity/pset/finalize.rs b/src/daemon/actions/simplicity/pset/finalize.rs new file mode 100644 index 0000000..4e4c29c --- /dev/null +++ b/src/daemon/actions/simplicity/pset/finalize.rs @@ -0,0 +1,56 @@ +use super::PsetError; +use crate::daemon::actions::types::{PsetFinalizeRequest, PsetFinalizeResponse}; +use crate::hal_simplicity::Program; +use crate::simplicity::jet; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PsetFinalizeError { + #[error(transparent)] + SharedError(#[from] PsetError), + + #[error("Failed to decode PSET: {0}")] + PsetDecode(elements::pset::ParseError), + + #[error("Failed to parse program: {0}")] + ProgramParse(simplicity::ParseError), + + #[error("Program does not have a redeem node")] + NoRedeemNode, + + #[error("Failed to prune program: {0}")] + ProgramPrune(simplicity::bit_machine::ExecutionError), +} + +pub fn finalize(req: PsetFinalizeRequest) -> Result { + let mut pset: elements::pset::PartiallySignedTransaction = + req.pset.parse().map_err(PsetFinalizeError::PsetDecode)?; + + let input_idx_usize = req.input_index as usize; + + let program = Program::::from_str(&req.program, Some(&req.witness)) + .map_err(PsetFinalizeError::ProgramParse)?; + + let (tx_env, control_block, tap_leaf) = super::execution_environment( + &pset, + input_idx_usize, + program.cmr(), + req.genesis_hash.as_deref(), + )?; + + let cb_serialized = control_block.serialize(); + + let redeem_node = program.redeem_node().ok_or_else(|| PsetFinalizeError::NoRedeemNode)?; + + let pruned = redeem_node.prune(&tx_env).map_err(PsetFinalizeError::ProgramPrune)?; + + let (prog, witness) = pruned.to_vec_with_witness(); + + let input = &mut pset.inputs_mut()[input_idx_usize]; + input.final_script_witness = Some(vec![witness, prog, tap_leaf.into_bytes(), cb_serialized]); + + Ok(PsetFinalizeResponse { + pset: pset.to_string(), + updated_values: vec!["final_script_witness".to_string()], + }) +} diff --git a/src/daemon/actions/simplicity/pset/mod.rs b/src/daemon/actions/simplicity/pset/mod.rs new file mode 100644 index 0000000..570db9b --- /dev/null +++ b/src/daemon/actions/simplicity/pset/mod.rs @@ -0,0 +1,112 @@ +mod create; +mod extract; +mod finalize; +mod run; +mod update_input; + +pub use create::create; +pub use extract::extract; +pub use finalize::finalize; +pub use run::run; +pub use update_input::update_input; + +use crate::simplicity::elements::Transaction; +use crate::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; +use crate::simplicity::Cmr; +use elements::hashes::Hash as _; +use elements::pset::PartiallySignedTransaction; +use elements::taproot::ControlBlock; +use elements::Script; +use std::sync::Arc; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PsetError { + #[error("Input index {index} out-of-range for PSET with {total} inputs")] + InputIndexOutOfRange { + index: usize, + total: usize, + }, + + #[error("Failed to parse genesis hash: {0}")] + GenesisHashParse(String), + + #[error("Could not find Simplicity leaf in PSET taptree with CMR {cmr}; did you forget to run 'simplicity pset update-input'?")] + MissingSimplicityLeaf { + cmr: String, + }, + + #[error("Failed to extract transaction from PSET: {0}")] + PsetExtract(String), + + #[error("witness_utxo field not populated for input {0}")] + MissingWitnessUtxo(usize), +} + +fn execution_environment( + pset: &PartiallySignedTransaction, + input_idx: usize, + cmr: Cmr, + genesis_hash: Option<&str>, +) -> Result<(ElementsEnv>, ControlBlock, Script), PsetError> { + let n_inputs = pset.n_inputs(); + let input = pset.inputs().get(input_idx).ok_or_else(|| PsetError::InputIndexOutOfRange { + index: input_idx, + total: n_inputs, + })?; + + // Default to Liquid Testnet genesis block + let genesis_hash = match genesis_hash { + Some(s) => s.parse().map_err(|e| PsetError::GenesisHashParse(format!("{}", e)))?, + None => elements::BlockHash::from_byte_array([ + 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, + 0x79, 0x3b, 0x5b, 0x5e, 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, + 0x8e, 0xda, 0x71, 0xa7, + ]), + }; + + let mut control_block_leaf = None; + for (cb, script_ver) in &input.tap_scripts { + if script_ver.1 == simplicity::leaf_version() && &script_ver.0[..] == cmr.as_ref() { + control_block_leaf = Some((cb.clone(), script_ver.0.clone())); + } + } + + let (control_block, tap_leaf) = match control_block_leaf { + Some((cb, leaf)) => (cb, leaf), + None => { + return Err(PsetError::MissingSimplicityLeaf { + cmr: cmr.to_string(), + }) + } + }; + + let tx = pset.extract_tx().map_err(|e| PsetError::PsetExtract(format!("{}", e)))?; + let tx = Arc::new(tx); + + let input_utxos = pset + .inputs() + .iter() + .enumerate() + .map(|(n, input)| match input.witness_utxo { + Some(ref utxo) => Ok(ElementsUtxo { + script_pubkey: utxo.script_pubkey.clone(), + asset: utxo.asset, + value: utxo.value, + }), + None => Err(PsetError::MissingWitnessUtxo(n)), + }) + .collect::, _>>()?; + + let tx_env = ElementsEnv::new( + tx, + input_utxos, + input_idx as u32, + cmr, + control_block.clone(), + None, + genesis_hash, + ); + + Ok((tx_env, control_block, tap_leaf)) +} diff --git a/src/daemon/actions/simplicity/pset/run.rs b/src/daemon/actions/simplicity/pset/run.rs new file mode 100644 index 0000000..c6cb564 --- /dev/null +++ b/src/daemon/actions/simplicity/pset/run.rs @@ -0,0 +1,109 @@ +use super::PsetError; +use crate::daemon::actions::types::{JetCall, PsetRunRequest, PsetRunResponse}; +use crate::hal_simplicity::Program; +use crate::simplicity::bit_machine::{BitMachine, ExecTracker}; +use crate::simplicity::jet; +use crate::simplicity::{Cmr, Ihr}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PsetRunError { + #[error(transparent)] + SharedError(#[from] PsetError), + + #[error("Failed to decode PSET: {0}")] + PsetDecode(elements::pset::ParseError), + + #[error("Failed to parse program: {0}")] + ProgramParse(simplicity::ParseError), + + #[error("Program does not have a redeem node")] + NoRedeemNode, + + #[error("Failed to construct bit machine: {0}")] + BitMachineConstruction(simplicity::bit_machine::LimitError), +} + +struct JetTracker(Vec); + +impl ExecTracker for JetTracker { + fn track_left(&mut self, _: Ihr) {} + fn track_right(&mut self, _: Ihr) {} + fn track_jet_call( + &mut self, + jet: &J, + input_buffer: &[simplicity::ffi::ffi::UWORD], + output_buffer: &[simplicity::ffi::ffi::UWORD], + success: bool, + ) { + let mut input_hex = String::new(); + for word in input_buffer.iter().rev() { + for byte in word.to_be_bytes() { + input_hex.push_str(&format!("{:02x}", byte)); + } + } + + let mut output_hex = String::new(); + for word in output_buffer.iter().rev() { + for byte in word.to_be_bytes() { + output_hex.push_str(&format!("{:02x}", byte)); + } + } + + let jet_name = jet.to_string(); + let equality_check = match jet_name.as_str() { + "eq_1" => None, + "eq_2" => None, + x if x.strip_prefix("eq_").is_some() => { + let split = input_hex.split_at(input_hex.len() / 2); + Some((split.0.to_owned(), split.1.to_owned())) + } + _ => None, + }; + + self.0.push(JetCall { + jet: jet_name, + source_ty: jet.source_ty().to_final().to_string(), + target_ty: jet.target_ty().to_final().to_string(), + success, + input_hex, + output_hex, + equality_check, + }); + } + + fn track_dbg_call(&mut self, _: &Cmr, _: simplicity::Value) {} + fn is_track_debug_enabled(&self) -> bool { + false + } +} + +pub fn run(req: PsetRunRequest) -> Result { + let pset: elements::pset::PartiallySignedTransaction = + req.pset.parse().map_err(PsetRunError::PsetDecode)?; + + let input_idx_usize = req.input_index as usize; + + let program = Program::::from_str(&req.program, Some(&req.witness)) + .map_err(PsetRunError::ProgramParse)?; + + let (tx_env, _control_block, _tap_leaf) = super::execution_environment( + &pset, + input_idx_usize, + program.cmr(), + req.genesis_hash.as_deref(), + )?; + + let redeem_node = program.redeem_node().ok_or_else(|| PsetRunError::NoRedeemNode)?; + + let mut mac = + BitMachine::for_program(redeem_node).map_err(PsetRunError::BitMachineConstruction)?; + + let mut tracker = JetTracker(vec![]); + let success = mac.exec_with_tracker(redeem_node, &tx_env, &mut tracker).is_ok(); + + Ok(PsetRunResponse { + success, + jets: tracker.0, + }) +} diff --git a/src/daemon/actions/simplicity/pset/update_input.rs b/src/daemon/actions/simplicity/pset/update_input.rs new file mode 100644 index 0000000..e53f572 --- /dev/null +++ b/src/daemon/actions/simplicity/pset/update_input.rs @@ -0,0 +1,191 @@ +use super::PsetError; +use crate::daemon::actions::types::{PsetUpdateInputRequest, PsetUpdateInputResponse}; +use crate::hal_simplicity::taproot_spend_info; +use core::str::FromStr; +use elements::bitcoin::secp256k1; +use elements::schnorr::XOnlyPublicKey; +use simplicity::hex::parse::FromHex as _; +use std::collections::BTreeMap; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PsetUpdateInputError { + #[error(transparent)] + SharedError(#[from] PsetError), + + #[error("Failed to decode PSET: {0}")] + PsetDecode(elements::pset::ParseError), + + #[error("Input index {index} out-of-range for PSET with {total} inputs")] + InputIndexOutOfRange { + index: usize, + total: usize, + }, + + #[error("Failed to parse CMR: {0}")] + CmrParse(elements::hashes::hex::HexToArrayError), + + #[error("Failed to parse internal key: {0}")] + InternalKeyParse(secp256k1::Error), + + #[error("Internal key must be present if CMR is; PSET requires a control block for each CMR, which in turn requires the internal key. If you don't know the internal key, good chance it is the BIP-0341 'unspendable key' 50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 or the web IDE's 'unspendable key' (highly discouraged for use in production) of f5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d2")] + MissingInternalKey, + + #[error("Input UTXO does not appear to be a Taproot output")] + NotTaprootOutput, + + #[error("Failed to parse 32-byte state commitment as hex: {0}")] + StateParse(elements::hashes::hex::HexToArrayError), + + #[error("CMR and internal key imply output key {output_key}, which does not match input scriptPubKey {script_pubkey}")] + OutputKeyMismatch { + output_key: String, + script_pubkey: String, + }, + + #[error("Failed to parse input UTXO: expected format ::")] + UtxoFormatInvalid, + + #[error("Failed to parse scriptPubKey hex: {0}")] + ScriptPubKeyParse(elements::hex::Error), + + #[error("Failed to parse asset hex: {0}")] + AssetParse(elements::hashes::hex::HexToArrayError), + + #[error("Failed to parse asset commitment hex: {0}")] + AssetCommitmentParse(elements::hashes::hex::HexToBytesError), + + #[error("Failed to decode asset commitment: {0}")] + AssetCommitmentDecode(elements::encode::Error), + + #[error("Failed to parse value commitment hex: {0}")] + ValueCommitmentParse(elements::hashes::hex::HexToBytesError), + + #[error("Failed to decode value commitment: {0}")] + ValueCommitmentDecode(elements::encode::Error), +} + +pub fn update_input( + req: PsetUpdateInputRequest, +) -> Result { + let mut pset: elements::pset::PartiallySignedTransaction = + req.pset.parse().map_err(PsetUpdateInputError::PsetDecode)?; + + let input_idx = req.input_index as usize; + let input_utxo = parse_elements_utxo(&req.input_utxo)?; + + let n_inputs = pset.n_inputs(); + let input = pset.inputs_mut().get_mut(input_idx).ok_or_else(|| { + PsetUpdateInputError::InputIndexOutOfRange { + index: input_idx, + total: n_inputs, + } + })?; + + let cmr = req + .cmr + .as_deref() + .map(simplicity::Cmr::from_str) + .transpose() + .map_err(PsetUpdateInputError::CmrParse)?; + + let internal_key = req + .internal_key + .as_deref() + .map(XOnlyPublicKey::from_str) + .transpose() + .map_err(PsetUpdateInputError::InternalKeyParse)?; + + if cmr.is_some() && internal_key.is_none() { + return Err(PsetUpdateInputError::MissingInternalKey); + } + + if !input_utxo.script_pubkey.is_v1_p2tr() { + return Err(PsetUpdateInputError::NotTaprootOutput); + } + + let state = req + .state + .as_deref() + .map(<[u8; 32]>::from_hex) + .transpose() + .map_err(PsetUpdateInputError::StateParse)?; + + let mut updated_values = vec![]; + if let Some(internal_key) = internal_key { + updated_values.push("tap_internal_key".to_string()); + input.tap_internal_key = Some(internal_key); + + if let Some(cmr) = cmr { + let spend_info = taproot_spend_info(internal_key, state, cmr); + if spend_info.output_key().as_inner().serialize() != input_utxo.script_pubkey[2..] { + return Err(PsetUpdateInputError::OutputKeyMismatch { + output_key: spend_info.output_key().as_inner().to_string(), + script_pubkey: input_utxo.script_pubkey.to_string(), + }); + } + + let script_ver = spend_info.as_script_map().keys().next().unwrap(); + let cb = spend_info.control_block(script_ver).unwrap(); + input.tap_merkle_root = spend_info.merkle_root(); + input.tap_scripts = BTreeMap::new(); + input.tap_scripts.insert(cb, script_ver.clone()); + updated_values.push("tap_merkle_root".to_string()); + updated_values.push("tap_scripts".to_string()); + } + } + + input.witness_utxo = Some(elements::TxOut { + asset: input_utxo.asset, + value: input_utxo.value, + nonce: elements::confidential::Nonce::Null, + script_pubkey: input_utxo.script_pubkey, + witness: elements::TxOutWitness::empty(), + }); + updated_values.push("witness_utxo".to_string()); + + Ok(PsetUpdateInputResponse { + pset: pset.to_string(), + updated_values, + }) +} + +fn parse_elements_utxo( + s: &str, +) -> Result { + use crate::simplicity::bitcoin::{Amount, Denomination}; + + let parts: Vec<&str> = s.split(':').collect(); + if parts.len() != 3 { + return Err(PsetUpdateInputError::UtxoFormatInvalid); + } + + let script_pubkey: elements::Script = + parts[0].parse().map_err(PsetUpdateInputError::ScriptPubKeyParse)?; + + let asset = if parts[1].len() == 64 { + let asset_id: elements::AssetId = + parts[1].parse().map_err(PsetUpdateInputError::AssetParse)?; + elements::confidential::Asset::Explicit(asset_id) + } else { + let commitment_bytes = + Vec::from_hex(parts[1]).map_err(PsetUpdateInputError::AssetCommitmentParse)?; + elements::confidential::Asset::from_commitment(&commitment_bytes) + .map_err(PsetUpdateInputError::AssetCommitmentDecode)? + }; + + let value = if let Ok(btc_amount) = Amount::from_str_in(parts[2], Denomination::Bitcoin) { + elements::confidential::Value::Explicit(btc_amount.to_sat()) + } else { + let commitment_bytes = + Vec::from_hex(parts[2]).map_err(PsetUpdateInputError::ValueCommitmentParse)?; + elements::confidential::Value::from_commitment(&commitment_bytes) + .map_err(PsetUpdateInputError::ValueCommitmentDecode)? + }; + + Ok(crate::simplicity::jet::elements::ElementsUtxo { + script_pubkey, + asset, + value, + }) +} diff --git a/src/daemon/actions/simplicity/sighash.rs b/src/daemon/actions/simplicity/sighash.rs new file mode 100644 index 0000000..585b60b --- /dev/null +++ b/src/daemon/actions/simplicity/sighash.rs @@ -0,0 +1,281 @@ +use crate::daemon::actions::types::{SimplicitySighashRequest, SimplicitySighashResponse}; +use crate::simplicity::bitcoin::secp256k1::{ + schnorr, Keypair, Message, Secp256k1, SecretKey, XOnlyPublicKey, +}; +use crate::simplicity::elements; +use crate::simplicity::elements::hex::FromHex; +use crate::simplicity::elements::taproot::ControlBlock; +use crate::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; +use crate::simplicity::Cmr; +use elements::bitcoin::secp256k1; +use elements::hashes::Hash as _; +use elements::pset::PartiallySignedTransaction; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum SimplicitySighashError { + #[error("Error extracting transaction from PSET: {0}")] + PsetExtraction(elements::pset::Error), + + #[error("Error parsing transaction hex: {0}")] + TransactionHexParsing(elements::hex::Error), + + #[error("Error decoding transaction: {0}")] + TransactionDecoding(elements::encode::Error), + + #[error("Error parsing CMR: {0}")] + CmrParsing(elements::hashes::hex::HexToArrayError), + + #[error("Error parsing control block hex: {0}")] + ControlBlockHexParsing(elements::hex::Error), + + #[error("Error decoding control block: {0}")] + ControlBlockDecoding(elements::taproot::TaprootError), + + #[error("Input index {index} out-of-range for PSET with {n_inputs} inputs")] + InputIndexOutOfRange { + index: u32, + n_inputs: usize, + }, + + #[error("Could not find control block in PSET for CMR {cmr}")] + ControlBlockNotFound { + cmr: String, + }, + + #[error("With a raw transaction, control-block must be provided")] + ControlBlockRequired, + + #[error("Witness UTXO field not populated for input {input}")] + WitnessUtxoMissing { + input: usize, + }, + + #[error("With a raw transaction, input-utxos must be provided")] + InputUtxosRequired, + + #[error("Expected {expected} input UTXOs but got {actual}")] + InputUtxoCountMismatch { + expected: usize, + actual: usize, + }, + + #[error("Error parsing genesis hash: {0}")] + GenesisHashParsing(elements::hashes::hex::HexToArrayError), + + #[error("Error parsing secret key: {0}")] + SecretKeyParsing(secp256k1::Error), + + #[error("Secret key had public key {derived}, but was passed explicit public key {provided}")] + PublicKeyMismatch { + derived: String, + provided: String, + }, + + #[error("Error parsing public key: {0}")] + PublicKeyParsing(secp256k1::Error), + + #[error("Error parsing signature: {0}")] + SignatureParsing(secp256k1::Error), + + #[error("If signature is provided, public-key must be provided as well")] + SignatureWithoutPublicKey, + + #[error("Error parsing input UTXO: {0}")] + InputUtxoParsing(String), + + #[error("Error parsing scriptPubKey hex: {0}")] + ScriptPubKeyParsing(elements::hex::Error), + + #[error("Error parsing asset hex: {0}")] + AssetHexParsing(elements::hashes::hex::HexToArrayError), + + #[error("Error parsing asset commitment hex: {0}")] + AssetCommitmentHexParsing(elements::hex::Error), + + #[error("Error decoding asset commitment: {0}")] + AssetCommitmentDecoding(elements::encode::Error), + + #[error("Error parsing value commitment hex: {0}")] + ValueCommitmentHexParsing(elements::hex::Error), + + #[error("Error decoding value commitment: {0}")] + ValueCommitmentDecoding(elements::encode::Error), +} + +pub fn sighash( + req: SimplicitySighashRequest, +) -> Result { + let secp = Secp256k1::new(); + + // Attempt to decode transaction as PSET first + let pset = req.tx.parse::().ok(); + + let tx = match pset { + Some(ref pset) => pset.extract_tx().map_err(SimplicitySighashError::PsetExtraction)?, + None => { + let tx_bytes = + Vec::from_hex(&req.tx).map_err(SimplicitySighashError::TransactionHexParsing)?; + elements::encode::deserialize(&tx_bytes) + .map_err(SimplicitySighashError::TransactionDecoding)? + } + }; + + let cmr: Cmr = req.cmr.parse().map_err(SimplicitySighashError::CmrParsing)?; + + // If the user specifies a control block, use it. Otherwise query the PSET. + let control_block = if let Some(cb) = req.control_block { + let cb_bytes = + Vec::from_hex(&cb).map_err(SimplicitySighashError::ControlBlockHexParsing)?; + ControlBlock::from_slice(&cb_bytes).map_err(SimplicitySighashError::ControlBlockDecoding)? + } else if let Some(ref pset) = pset { + let n_inputs = pset.n_inputs(); + let input = pset.inputs().get(req.input_index as usize).ok_or_else(|| { + SimplicitySighashError::InputIndexOutOfRange { + index: req.input_index, + n_inputs, + } + })?; + + let mut control_block = None; + for (cb, script_ver) in &input.tap_scripts { + if script_ver.1 == simplicity::leaf_version() && &script_ver.0[..] == cmr.as_ref() { + control_block = Some(cb.clone()); + } + } + control_block.ok_or_else(|| SimplicitySighashError::ControlBlockNotFound { + cmr: cmr.to_string(), + })? + } else { + return Err(SimplicitySighashError::ControlBlockRequired); + }; + + let input_utxos = if let Some(input_utxos) = req.input_utxos { + input_utxos + .iter() + .map(|utxo_str| parse_elements_utxo(utxo_str)) + .collect::, _>>()? + } else if let Some(ref pset) = pset { + pset.inputs() + .iter() + .enumerate() + .map(|(n, input)| match input.witness_utxo { + Some(ref utxo) => Ok(ElementsUtxo { + script_pubkey: utxo.script_pubkey.clone(), + asset: utxo.asset, + value: utxo.value, + }), + None => Err(SimplicitySighashError::WitnessUtxoMissing { + input: n, + }), + }) + .collect::, _>>()? + } else { + return Err(SimplicitySighashError::InputUtxosRequired); + }; + + if input_utxos.len() != tx.input.len() { + return Err(SimplicitySighashError::InputUtxoCountMismatch { + expected: tx.input.len(), + actual: input_utxos.len(), + }); + } + + // Default to Bitcoin blockhash + let genesis_hash = match req.genesis_hash { + Some(s) => s.parse().map_err(SimplicitySighashError::GenesisHashParsing)?, + None => elements::BlockHash::from_byte_array([ + 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, + 0x79, 0x3b, 0x5b, 0x5e, 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, + 0x8e, 0xda, 0x71, 0xa7, + ]), + }; + + let tx_env = + ElementsEnv::new(&tx, input_utxos, req.input_index, cmr, control_block, None, genesis_hash); + + let (pk, sig) = match (req.public_key, req.signature) { + (Some(pk), None) => ( + Some(pk.parse::().map_err(SimplicitySighashError::PublicKeyParsing)?), + None, + ), + (Some(pk), Some(sig)) => ( + Some(pk.parse::().map_err(SimplicitySighashError::PublicKeyParsing)?), + Some( + sig.parse::() + .map_err(SimplicitySighashError::SignatureParsing)?, + ), + ), + (None, Some(_)) => return Err(SimplicitySighashError::SignatureWithoutPublicKey), + (None, None) => (None, None), + }; + + let sighash = tx_env.c_tx_env().sighash_all(); + let sighash_msg = Message::from_digest(sighash.to_byte_array()); + + Ok(SimplicitySighashResponse { + sighash, + signature: match req.secret_key { + Some(sk) => { + let sk: SecretKey = sk.parse().map_err(SimplicitySighashError::SecretKeyParsing)?; + let keypair = Keypair::from_secret_key(&secp, &sk); + + if let Some(ref pk) = pk { + if pk != &keypair.x_only_public_key().0 { + return Err(SimplicitySighashError::PublicKeyMismatch { + derived: keypair.x_only_public_key().0.to_string(), + provided: pk.to_string(), + }); + } + } + + Some(secp.sign_schnorr(&sighash_msg, &keypair)) + } + None => None, + }, + valid_signature: match (pk, sig) { + (Some(pk), Some(sig)) => Some(secp.verify_schnorr(&sig, &sighash_msg, &pk).is_ok()), + _ => None, + }, + }) +} + +fn parse_elements_utxo(s: &str) -> Result { + use crate::simplicity::bitcoin::{Amount, Denomination}; + + let parts: Vec<&str> = s.split(':').collect(); + if parts.len() != 3 { + return Err(SimplicitySighashError::InputUtxoParsing( + "expected format ::".to_string(), + )); + } + + let script_pubkey: elements::Script = + parts[0].parse().map_err(SimplicitySighashError::ScriptPubKeyParsing)?; + + let asset = if parts[1].len() == 64 { + let asset_id: elements::AssetId = + parts[1].parse().map_err(SimplicitySighashError::AssetHexParsing)?; + elements::confidential::Asset::Explicit(asset_id) + } else { + let commitment_bytes = + Vec::from_hex(parts[1]).map_err(SimplicitySighashError::AssetCommitmentHexParsing)?; + elements::confidential::Asset::from_commitment(&commitment_bytes) + .map_err(SimplicitySighashError::AssetCommitmentDecoding)? + }; + + let value = if let Ok(btc_amount) = Amount::from_str_in(parts[2], Denomination::Bitcoin) { + elements::confidential::Value::Explicit(btc_amount.to_sat()) + } else { + let commitment_bytes = + Vec::from_hex(parts[2]).map_err(SimplicitySighashError::ValueCommitmentHexParsing)?; + elements::confidential::Value::from_commitment(&commitment_bytes) + .map_err(SimplicitySighashError::ValueCommitmentDecoding)? + }; + + Ok(ElementsUtxo { + script_pubkey, + asset, + value, + }) +} diff --git a/src/daemon/actions/tx.rs b/src/daemon/actions/tx.rs new file mode 100644 index 0000000..72e0978 --- /dev/null +++ b/src/daemon/actions/tx.rs @@ -0,0 +1,532 @@ +use std::convert::TryInto; + +use elements::bitcoin::{self, secp256k1}; +use elements::encode::{deserialize, serialize}; +use elements::hashes::Hash; +use elements::secp256k1_zkp::{ + Generator, PedersenCommitment, PublicKey, RangeProof, SurjectionProof, Tweak, +}; +use elements::{ + confidential, AssetIssuance, OutPoint, Script, Transaction, TxIn, TxInWitness, TxOut, + TxOutWitness, +}; +use hex::FromHexError; +use thiserror::Error; + +use crate::confidential::{ + ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialType, ConfidentialValueInfo, +}; +use crate::tx::{ + AssetIssuanceInfo, InputInfo, InputScriptInfo, InputWitnessInfo, OutputInfo, OutputScriptInfo, + OutputWitnessInfo, PeginDataInfo, PegoutDataInfo, TransactionInfo, +}; +use crate::{GetInfo, Network}; + +use super::types::{TxCreateRequest, TxDecodeRequest}; + +#[derive(Debug, Error)] +pub enum TxError { + #[error("Failed to parse transaction info JSON: {0}")] + JsonParse(serde_json::Error), + + #[error("Failed to decode raw transaction hex: {0}")] + TxHex(FromHexError), + + #[error("Invalid transaction format: {0}")] + TxDeserialize(elements::encode::Error), + + #[error("Failed to serialize response: {0}")] + Serialize(serde_json::Error), + + #[error("Failed to parse network: {0}")] + NetworkParse(String), + + #[error("{field} is required")] + MissingField { + field: String, + }, + + #[error("Invalid prevout format: {0}")] + PrevoutParse(bitcoin::blockdata::transaction::ParseOutPointError), + + #[error("txid field given without vout field")] + MissingVout, + + #[error("Conflicting prevout information")] + ConflictingPrevout, + + #[error("No previous output provided")] + NoPrevout, + + #[error("Invalid confidential commitment: {0}")] + ConfidentialCommitment(elements::secp256k1_zkp::Error), + + #[error("Invalid confidential publicKey: {0}")] + ConfidentialCommitmentPublicKey(secp256k1::Error), + + #[error("Wrong size of nonce field")] + NonceSize, + + #[error("Invalid size of asset_entropy")] + AssetEntropySize, + + #[error("Invalid asset_blinding_nonce: {0}")] + AssetBlindingNonce(elements::secp256k1_zkp::Error), + + #[error("Decoding script assembly is not yet supported")] + AsmNotSupported, + + #[error("No scriptSig info provided")] + NoScriptSig, + + #[error("No scriptPubKey info provided")] + NoScriptPubKey, + + #[error("Invalid outpoint in pegin_data: {0}")] + PeginOutpoint(bitcoin::blockdata::transaction::ParseOutPointError), + + #[error("Outpoint in pegin_data does not correspond to input value")] + PeginOutpointMismatch, + + #[error("Asset in pegin_data should be explicit")] + PeginAssetNotExplicit, + + #[error("Invalid rangeproof: {0}")] + RangeProof(elements::secp256k1_zkp::Error), + + #[error("Invalid sequence: {0}")] + Sequence(core::num::TryFromIntError), + + #[error("Addresses for different networks are used in the output scripts")] + MixedNetworks, + + #[error("Invalid surjection proof: {0}")] + SurjectionProof(elements::secp256k1_zkp::Error), + + #[error("Value in pegout_data does not correspond to output value")] + PegoutValueMismatch, + + #[error("Explicit value is required for pegout data")] + PegoutValueNotExplicit, + + #[error("Asset in pegout_data does not correspond to output value")] + PegoutAssetMismatch, +} + +pub fn create(req: TxCreateRequest) -> Result { + let info = serde_json::from_str::(&req.tx_info).map_err(TxError::JsonParse)?; + + let tx = create_transaction(info)?; + let tx_bytes = serialize(&tx); + Ok(hex::encode(&tx_bytes)) +} + +pub fn decode(req: TxDecodeRequest) -> Result { + let raw_tx = hex::decode(&req.raw_tx).map_err(TxError::TxHex)?; + + let tx: Transaction = deserialize(&raw_tx).map_err(TxError::TxDeserialize)?; + + let network = + req.network.as_deref().map(parse_network).transpose()?.unwrap_or(Network::ElementsRegtest); + + let info = GetInfo::get_info(&tx, network); + serde_json::to_value(&info).map_err(TxError::Serialize) +} + +pub fn create_transaction(info: TransactionInfo) -> Result { + Ok(Transaction { + version: info.version.ok_or(TxError::MissingField { + field: "version".to_string(), + })?, + lock_time: info.locktime.ok_or(TxError::MissingField { + field: "locktime".to_string(), + })?, + input: info + .inputs + .ok_or(TxError::MissingField { + field: "inputs".to_string(), + })? + .into_iter() + .map(create_input) + .collect::, _>>()?, + output: info + .outputs + .ok_or(TxError::MissingField { + field: "outputs".to_string(), + })? + .into_iter() + .map(create_output) + .collect::, _>>()?, + }) +} + +fn outpoint_from_input_info(input: &InputInfo) -> Result { + let op1: Option = + input.prevout.as_ref().map(|op| op.parse().map_err(TxError::PrevoutParse)).transpose()?; + + let op2 = match input.txid { + Some(txid) => match input.vout { + Some(vout) => Some(OutPoint { + txid, + vout, + }), + None => return Err(TxError::MissingVout), + }, + None => None, + }; + + match (op1, op2) { + (Some(op1), Some(op2)) => { + if op1 != op2 { + return Err(TxError::ConflictingPrevout); + } + Ok(op1) + } + (Some(op), None) => Ok(op), + (None, Some(op)) => Ok(op), + (None, None) => Err(TxError::NoPrevout), + } +} + +fn bytes_32(bytes: &[u8]) -> Option<[u8; 32]> { + if bytes.len() != 32 { + None + } else { + let mut array = [0; 32]; + for (x, y) in bytes.iter().zip(array.iter_mut()) { + *y = *x; + } + Some(array) + } +} + +fn create_confidential_value(info: ConfidentialValueInfo) -> Result { + Ok(match info.type_ { + ConfidentialType::Null => confidential::Value::Null, + ConfidentialType::Explicit => { + confidential::Value::Explicit(info.value.ok_or(TxError::MissingField { + field: "value".to_string(), + })?) + } + ConfidentialType::Confidential => { + let comm = PedersenCommitment::from_slice( + &info + .commitment + .ok_or(TxError::MissingField { + field: "commitment".to_string(), + })? + .0[..], + ) + .map_err(TxError::ConfidentialCommitment)?; + confidential::Value::Confidential(comm) + } + }) +} + +fn create_confidential_asset(info: ConfidentialAssetInfo) -> Result { + Ok(match info.type_ { + ConfidentialType::Null => confidential::Asset::Null, + ConfidentialType::Explicit => { + confidential::Asset::Explicit(info.asset.ok_or(TxError::MissingField { + field: "asset".to_string(), + })?) + } + ConfidentialType::Confidential => { + let gen = Generator::from_slice( + &info + .commitment + .ok_or(TxError::MissingField { + field: "commitment".to_string(), + })? + .0[..], + ) + .map_err(TxError::ConfidentialCommitment)?; + confidential::Asset::Confidential(gen) + } + }) +} + +fn create_confidential_nonce(info: ConfidentialNonceInfo) -> Result { + Ok(match info.type_ { + ConfidentialType::Null => confidential::Nonce::Null, + ConfidentialType::Explicit => confidential::Nonce::Explicit( + bytes_32( + &info + .nonce + .ok_or(TxError::MissingField { + field: "nonce".to_string(), + })? + .0[..], + ) + .ok_or(TxError::NonceSize)?, + ), + ConfidentialType::Confidential => { + let pubkey = PublicKey::from_slice( + &info + .commitment + .ok_or(TxError::MissingField { + field: "commitment".to_string(), + })? + .0[..], + ) + .map_err(TxError::ConfidentialCommitmentPublicKey)?; + confidential::Nonce::Confidential(pubkey) + } + }) +} + +fn create_asset_issuance(info: AssetIssuanceInfo) -> Result { + Ok(AssetIssuance { + asset_blinding_nonce: Tweak::from_slice( + &info + .asset_blinding_nonce + .ok_or(TxError::MissingField { + field: "asset_blinding_nonce".to_string(), + })? + .0[..], + ) + .map_err(TxError::AssetBlindingNonce)?, + asset_entropy: bytes_32( + &info + .asset_entropy + .ok_or(TxError::MissingField { + field: "asset_entropy".to_string(), + })? + .0[..], + ) + .ok_or(TxError::AssetEntropySize)?, + amount: create_confidential_value(info.amount.ok_or(TxError::MissingField { + field: "amount".to_string(), + })?)?, + inflation_keys: create_confidential_value(info.inflation_keys.ok_or( + TxError::MissingField { + field: "inflation_keys".to_string(), + }, + )?)?, + }) +} + +fn create_script_sig(ss: InputScriptInfo) -> Result { + if let Some(hex) = ss.hex { + Ok(hex.0.into()) + } else if ss.asm.is_some() { + Err(TxError::AsmNotSupported) + } else { + Err(TxError::NoScriptSig) + } +} + +fn create_pegin_witness( + pd: PeginDataInfo, + prevout: bitcoin::OutPoint, +) -> Result>, TxError> { + let parsed_outpoint: bitcoin::OutPoint = pd.outpoint.parse().map_err(TxError::PeginOutpoint)?; + + if prevout != parsed_outpoint { + return Err(TxError::PeginOutpointMismatch); + } + + let asset = match create_confidential_asset(pd.asset)? { + confidential::Asset::Explicit(asset) => asset, + _ => return Err(TxError::PeginAssetNotExplicit), + }; + + Ok(vec![ + serialize(&pd.value), + serialize(&asset), + pd.genesis_hash.to_byte_array().to_vec(), + serialize(&pd.claim_script.0), + serialize(&pd.mainchain_tx_hex.0), + serialize(&pd.merkle_proof.0), + ]) +} + +fn convert_outpoint_to_btc(p: elements::OutPoint) -> bitcoin::OutPoint { + bitcoin::OutPoint { + txid: bitcoin::Txid::from_byte_array(p.txid.to_byte_array()), + vout: p.vout, + } +} + +fn create_input_witness( + info: Option, + pd: Option, + prevout: OutPoint, +) -> Result { + let pegin_witness = + if let Some(info_wit) = info.as_ref().and_then(|info| info.pegin_witness.as_ref()) { + info_wit.iter().map(|h| h.clone().0).collect() + } else if let Some(pd) = pd { + create_pegin_witness(pd, convert_outpoint_to_btc(prevout))? + } else { + Default::default() + }; + + if let Some(wi) = info { + Ok(TxInWitness { + amount_rangeproof: wi + .amount_rangeproof + .map(|b| { + RangeProof::from_slice(&b.0).map(|rp| Box::new(rp)).map_err(TxError::RangeProof) + }) + .transpose()?, + inflation_keys_rangeproof: wi + .inflation_keys_rangeproof + .map(|b| { + RangeProof::from_slice(&b.0).map(|rp| Box::new(rp)).map_err(TxError::RangeProof) + }) + .transpose()?, + script_witness: match wi.script_witness { + Some(ref w) => w.iter().map(|h| h.clone().0).collect(), + None => Vec::new(), + }, + pegin_witness, + }) + } else { + Ok(TxInWitness { + pegin_witness, + ..Default::default() + }) + } +} + +fn create_input(input: InputInfo) -> Result { + let has_issuance = input.has_issuance.unwrap_or(input.asset_issuance.is_some()); + let is_pegin = input.is_pegin.unwrap_or(input.pegin_data.is_some()); + let prevout = outpoint_from_input_info(&input)?; + + Ok(TxIn { + previous_output: prevout, + script_sig: input.script_sig.map(create_script_sig).transpose()?.unwrap_or_default(), + sequence: elements::Sequence::from_height( + input.sequence.unwrap_or_default().try_into().map_err(TxError::Sequence)?, + ), + is_pegin, + asset_issuance: if has_issuance { + input.asset_issuance.map(create_asset_issuance).transpose()?.unwrap_or_default() + } else { + Default::default() + }, + witness: create_input_witness(input.witness, input.pegin_data, prevout)?, + }) +} + +fn create_script_pubkey( + spk: OutputScriptInfo, + used_network: &mut Option, +) -> Result { + if let Some(hex) = spk.hex { + Ok(hex.0.into()) + } else if spk.asm.is_some() { + Err(TxError::AsmNotSupported) + } else if let Some(address) = spk.address { + // Error if another network had already been used. + if let Some(network) = Network::from_params(address.params) { + if used_network.replace(network).unwrap_or(network) != network { + return Err(TxError::MixedNetworks); + } + } + + Ok(address.script_pubkey()) + } else { + Err(TxError::NoScriptPubKey) + } +} + +fn create_bitcoin_script_pubkey( + spk: hal::tx::OutputScriptInfo, +) -> Result { + if let Some(hex) = spk.hex { + Ok(hex.0.into()) + } else if spk.asm.is_some() { + Err(TxError::AsmNotSupported) + } else if let Some(address) = spk.address { + Ok(address.assume_checked().script_pubkey()) + } else { + Err(TxError::NoScriptPubKey) + } +} + +fn create_output_witness(w: OutputWitnessInfo) -> Result { + Ok(TxOutWitness { + surjection_proof: w + .surjection_proof + .map(|b| { + SurjectionProof::from_slice(&b.0[..]) + .map(|sp| Box::new(sp)) + .map_err(TxError::SurjectionProof) + }) + .transpose()?, + rangeproof: w + .rangeproof + .map(|b| { + RangeProof::from_slice(&b.0[..]).map(|rp| Box::new(rp)).map_err(TxError::RangeProof) + }) + .transpose()?, + }) +} + +fn create_script_pubkey_from_pegout_data(pd: PegoutDataInfo) -> Result { + let mut builder = elements::script::Builder::new() + .push_opcode(elements::opcodes::all::OP_RETURN) + .push_slice(&pd.genesis_hash.to_byte_array()) + .push_slice(create_bitcoin_script_pubkey(pd.script_pub_key)?.as_bytes()); + for d in pd.extra_data { + builder = builder.push_slice(&d.0); + } + Ok(builder.into_script()) +} + +fn create_output(output: OutputInfo) -> Result { + let mut used_network = None; + let value = output + .value + .ok_or(TxError::MissingField { + field: "value".to_string(), + }) + .and_then(create_confidential_value)?; + let asset = output + .asset + .ok_or(TxError::MissingField { + field: "asset".to_string(), + }) + .and_then(create_confidential_asset)?; + + Ok(TxOut { + asset, + value, + nonce: output + .nonce + .map(create_confidential_nonce) + .transpose()? + .unwrap_or(confidential::Nonce::Null), + script_pubkey: if let Some(spk) = output.script_pub_key { + create_script_pubkey(spk, &mut used_network)? + } else if let Some(pd) = output.pegout_data { + match value { + confidential::Value::Explicit(v) => { + if v != pd.value { + return Err(TxError::PegoutValueMismatch); + } + } + _ => return Err(TxError::PegoutValueNotExplicit), + } + if asset != create_confidential_asset(pd.asset.clone())? { + return Err(TxError::PegoutAssetMismatch); + } + create_script_pubkey_from_pegout_data(pd)? + } else { + Default::default() + }, + witness: output.witness.map(create_output_witness).transpose()?.unwrap_or_default(), + }) +} + +fn parse_network(s: &str) -> Result { + match s.to_lowercase().as_str() { + "liquid" => Ok(Network::Liquid), + "liquid-testnet" | "liquidtestnet" => Ok(Network::LiquidTestnet), + "elementsregtest" | "elements-regtest" | "regtest" => Ok(Network::ElementsRegtest), + _ => Err(TxError::NetworkParse(format!("unknown network: {}", s))), + } +} diff --git a/src/daemon/actions/types.rs b/src/daemon/actions/types.rs new file mode 100644 index 0000000..33af263 --- /dev/null +++ b/src/daemon/actions/types.rs @@ -0,0 +1,190 @@ +use serde::{Deserialize, Serialize}; + +// Re-exports for proper serialization +pub use elements::bitcoin::secp256k1; +pub use elements::hashes::sha256; +pub use simplicity::bitcoin::secp256k1::schnorr; +pub use simplicity::{Amr, Cmr, Ihr}; + +// Address types +#[derive(Debug, Serialize, Deserialize)] +pub struct AddressCreateRequest { + pub network: Option, + pub pubkey: Option, + pub script: Option, + pub blinder: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AddressInspectRequest { + pub address: String, +} + +// Block types +#[derive(Debug, Serialize, Deserialize)] +pub struct BlockCreateRequest { + pub block_info: String, // JSON string + pub raw_stdout: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BlockDecodeRequest { + pub raw_block: String, + pub network: Option, + pub txids: Option, +} + +// Transaction types +#[derive(Debug, Serialize, Deserialize)] +pub struct TxCreateRequest { + pub tx_info: String, // JSON string + pub raw_stdout: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TxDecodeRequest { + pub raw_tx: String, + pub network: Option, +} + +// Keypair types +#[derive(Debug, Serialize, Deserialize)] +pub struct KeypairGenerateRequest {} + +#[derive(Debug, Serialize, Deserialize)] +pub struct KeypairGenerateResponse { + pub secret: secp256k1::SecretKey, + pub x_only: secp256k1::XOnlyPublicKey, + pub parity: secp256k1::Parity, +} + +// Simplicity types +#[derive(Debug, Serialize, Deserialize)] +pub struct SimplicityInfoRequest { + pub program: String, + pub witness: Option, + pub state: Option, + pub network: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SimplicityInfoResponse { + pub jets: &'static str, + pub commit_base64: String, + pub commit_decode: String, + pub type_arrow: String, + pub cmr: Cmr, + pub liquid_address_unconf: String, + pub liquid_testnet_address_unconf: String, + pub is_redeem: bool, + pub redeem_info: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RedeemInfo { + pub redeem_base64: String, + pub witness_hex: String, + pub amr: Amr, + pub ihr: Ihr, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SimplicitySighashRequest { + pub tx: String, + pub input_index: u32, + pub cmr: String, + pub control_block: Option, + pub genesis_hash: Option, + pub secret_key: Option, + pub public_key: Option, + pub signature: Option, + pub input_utxos: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SimplicitySighashResponse { + pub sighash: sha256::Hash, + pub signature: Option, + pub valid_signature: Option, +} + +// PSET types +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetCreateRequest { + pub inputs: String, // JSON array string + pub outputs: String, // JSON array string + pub network: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetCreateResponse { + pub pset: String, + pub updated_values: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetExtractRequest { + pub pset: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetExtractResponse { + pub raw_tx: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetFinalizeRequest { + pub pset: String, + pub input_index: u32, + pub program: String, + pub witness: String, + pub genesis_hash: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetFinalizeResponse { + pub pset: String, + pub updated_values: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetRunRequest { + pub pset: String, + pub input_index: u32, + pub program: String, + pub witness: String, + pub genesis_hash: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetRunResponse { + pub success: bool, + pub jets: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct JetCall { + pub jet: String, + pub source_ty: String, + pub target_ty: String, + pub success: bool, + pub input_hex: String, + pub output_hex: String, + pub equality_check: Option<(String, String)>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetUpdateInputRequest { + pub pset: String, + pub input_index: u32, + pub input_utxo: String, + pub internal_key: Option, + pub cmr: Option, + pub state: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PsetUpdateInputResponse { + pub pset: String, + pub updated_values: Vec, +} diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index 0a4a4ff..41ce36e 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -1,3 +1,4 @@ +mod actions; mod handler; use std::net::SocketAddr; From 767c69439e30d3a847730e208dd6acf166efd090 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 14:09:22 +0200 Subject: [PATCH 04/39] Connect actions to the RPC handler --- src/daemon/actions/mod.rs | 4 +- src/daemon/handler.rs | 187 +++++++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 6 deletions(-) diff --git a/src/daemon/actions/mod.rs b/src/daemon/actions/mod.rs index 5dc3157..028c9b1 100644 --- a/src/daemon/actions/mod.rs +++ b/src/daemon/actions/mod.rs @@ -1,9 +1,7 @@ -mod types; +pub mod types; pub mod address; pub mod block; pub mod keypair; pub mod simplicity; pub mod tx; - -pub use types::*; diff --git a/src/daemon/handler.rs b/src/daemon/handler.rs index 14a0b95..5f9451d 100644 --- a/src/daemon/handler.rs +++ b/src/daemon/handler.rs @@ -1,13 +1,173 @@ use crate::jsonrpc::{ErrorCode, JsonRpcService, RpcError, RpcHandler}; use serde_json::Value; +use super::actions::{self, types::*}; + +/// RPC method names +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RpcMethod { + AddressCreate, + AddressInspect, + BlockCreate, + BlockDecode, + TxCreate, + TxDecode, + KeypairGenerate, + SimplicityInfo, + SimplicitySighash, + PsetCreate, + PsetExtract, + PsetFinalize, + PsetRun, + PsetUpdateInput, +} + +impl RpcMethod { + pub fn from_str(s: &str) -> Option { + match s { + "address_create" => Some(Self::AddressCreate), + "address_inspect" => Some(Self::AddressInspect), + "block_create" => Some(Self::BlockCreate), + "block_decode" => Some(Self::BlockDecode), + "tx_create" => Some(Self::TxCreate), + "tx_decode" => Some(Self::TxDecode), + "keypair_generate" => Some(Self::KeypairGenerate), + "simplicity_info" => Some(Self::SimplicityInfo), + "simplicity_sighash" => Some(Self::SimplicitySighash), + "pset_create" => Some(Self::PsetCreate), + "pset_extract" => Some(Self::PsetExtract), + "pset_finalize" => Some(Self::PsetFinalize), + "pset_run" => Some(Self::PsetRun), + "pset_update_input" => Some(Self::PsetUpdateInput), + _ => None, + } + } + + #[allow(dead_code)] + pub fn as_str(&self) -> &'static str { + match self { + Self::AddressCreate => "address_create", + Self::AddressInspect => "address_inspect", + Self::BlockCreate => "block_create", + Self::BlockDecode => "block_decode", + Self::TxCreate => "tx_create", + Self::TxDecode => "tx_decode", + Self::KeypairGenerate => "keypair_generate", + Self::SimplicityInfo => "simplicity_info", + Self::SimplicitySighash => "simplicity_sighash", + Self::PsetCreate => "pset_create", + Self::PsetExtract => "pset_extract", + Self::PsetFinalize => "pset_finalize", + Self::PsetRun => "pset_run", + Self::PsetUpdateInput => "pset_update_input", + } + } +} + /// Default RPC handler that provides basic methods pub struct DefaultRpcHandler; impl RpcHandler for DefaultRpcHandler { - fn handle(&self, method: &str, _params: Option) -> Result { - match method { - _ => Err(RpcError::new(ErrorCode::MethodNotFound)), + fn handle(&self, method: &str, params: Option) -> Result { + let rpc_method = + RpcMethod::from_str(method).ok_or_else(|| RpcError::new(ErrorCode::MethodNotFound))?; + + match rpc_method { + RpcMethod::AddressCreate => { + let req: AddressCreateRequest = parse_params(params)?; + let result = actions::address::create(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::AddressInspect => { + let req: AddressInspectRequest = parse_params(params)?; + let result = actions::address::inspect(&req.address).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::BlockCreate => { + let req: BlockCreateRequest = parse_params(params)?; + let result = actions::block::create(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::BlockDecode => { + let req: BlockDecodeRequest = parse_params(params)?; + let result = actions::block::decode(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + Ok(result) + } + RpcMethod::TxCreate => { + let req: TxCreateRequest = parse_params(params)?; + let result = actions::tx::create(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::TxDecode => { + let req: TxDecodeRequest = parse_params(params)?; + let result = actions::tx::decode(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + Ok(result) + } + RpcMethod::KeypairGenerate => { + let result = actions::keypair::generate(); + serialize_result(result) + } + RpcMethod::SimplicityInfo => { + let req: SimplicityInfoRequest = parse_params(params)?; + let result = actions::simplicity::info(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::SimplicitySighash => { + let req: SimplicitySighashRequest = parse_params(params)?; + let result = actions::simplicity::sighash(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::PsetCreate => { + let req: PsetCreateRequest = parse_params(params)?; + let result = actions::simplicity::create(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::PsetExtract => { + let req: PsetExtractRequest = parse_params(params)?; + let result = actions::simplicity::extract(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::PsetFinalize => { + let req: PsetFinalizeRequest = parse_params(params)?; + let result = actions::simplicity::finalize(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::PsetRun => { + let req: PsetRunRequest = parse_params(params)?; + let result = actions::simplicity::run(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } + RpcMethod::PsetUpdateInput => { + let req: PsetUpdateInputRequest = parse_params(params)?; + let result = actions::simplicity::update_input(req).map_err(|e| { + RpcError::custom(ErrorCode::InternalError.code(), e.to_string()) + })?; + serialize_result(result) + } } } } @@ -18,6 +178,27 @@ impl DefaultRpcHandler { } } +/// Parse parameters from JSON value +fn parse_params(params: Option) -> Result { + let params = params.ok_or_else(|| { + RpcError::custom(ErrorCode::InvalidParams.code(), "Missing parameters".to_string()) + })?; + + serde_json::from_value(params).map_err(|e| { + RpcError::custom(ErrorCode::InvalidParams.code(), format!("Invalid parameters: {}", e)) + }) +} + +/// Serialize result to JSON value +fn serialize_result(result: T) -> Result { + serde_json::to_value(result).map_err(|e| { + RpcError::custom( + ErrorCode::InternalError.code(), + format!("Failed to serialize result: {}", e), + ) + }) +} + /// Create a JSONRPC service with the default handler pub fn create_service() -> JsonRpcService { JsonRpcService::new(DefaultRpcHandler::new()) From e0521232e24644fe7ba4af21ac230d1b8d25cbde Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 15:37:26 +0200 Subject: [PATCH 05/39] Refactor network handling: move Network and GetInfo to utils module --- src/address.rs | 2 +- src/bin/hal-simplicity/cmd/mod.rs | 2 +- src/bin/hal-simplicity/cmd/tx.rs | 2 +- src/bin/hal-simplicity/main.rs | 2 +- src/block.rs | 3 +- src/confidential.rs | 3 +- src/daemon/actions/address.rs | 2 +- src/daemon/actions/block.rs | 6 ++-- src/daemon/actions/tx.rs | 3 +- src/hal_simplicity.rs | 4 +-- src/lib.rs | 57 +------------------------------ src/tx.rs | 4 ++- src/utils.rs | 53 ++++++++++++++++++++++++++++ 13 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 src/utils.rs diff --git a/src/address.rs b/src/address.rs index ca8fae7..8d91dc7 100644 --- a/src/address.rs +++ b/src/address.rs @@ -2,7 +2,7 @@ use elements::bitcoin::{secp256k1, PublicKey}; use elements::{Address, PubkeyHash, Script, ScriptHash, WPubkeyHash, WScriptHash}; use serde::{Deserialize, Serialize}; -use crate::Network; +use crate::utils::Network; #[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] pub struct AddressInfo { diff --git a/src/bin/hal-simplicity/cmd/mod.rs b/src/bin/hal-simplicity/cmd/mod.rs index d706e18..78e7a6e 100644 --- a/src/bin/hal-simplicity/cmd/mod.rs +++ b/src/bin/hal-simplicity/cmd/mod.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::io; use std::io::Read; -use hal_simplicity::Network; +use hal_simplicity::utils::Network; /// Build a list of all built-in subcommands. pub fn subcommands<'a>() -> Vec> { diff --git a/src/bin/hal-simplicity/cmd/tx.rs b/src/bin/hal-simplicity/cmd/tx.rs index 191c256..f6c3f4d 100644 --- a/src/bin/hal-simplicity/cmd/tx.rs +++ b/src/bin/hal-simplicity/cmd/tx.rs @@ -22,7 +22,7 @@ use hal_simplicity::tx::{ AssetIssuanceInfo, InputInfo, InputScriptInfo, InputWitnessInfo, OutputInfo, OutputScriptInfo, OutputWitnessInfo, PeginDataInfo, PegoutDataInfo, TransactionInfo, }; -use hal_simplicity::Network; +use hal_simplicity::utils::Network; pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("tx", "manipulate transactions") diff --git a/src/bin/hal-simplicity/main.rs b/src/bin/hal-simplicity/main.rs index ea94b7e..f35f231 100644 --- a/src/bin/hal-simplicity/main.rs +++ b/src/bin/hal-simplicity/main.rs @@ -3,7 +3,7 @@ use std::process; pub use elements::bitcoin; -pub use hal_simplicity::{GetInfo, Network}; +pub use hal_simplicity::utils::{GetInfo, Network}; pub mod cmd; diff --git a/src/block.rs b/src/block.rs index 7127086..d850ac9 100644 --- a/src/block.rs +++ b/src/block.rs @@ -2,7 +2,8 @@ use elements::hashes::sha256; use elements::{dynafed, Block, BlockExtData, BlockHash, BlockHeader, TxMerkleNode, Txid}; use serde::{Deserialize, Serialize}; -use crate::{GetInfo, HexBytes, Network}; +use crate::utils::{GetInfo, Network}; +use hal::HexBytes; use crate::tx::TransactionInfo; diff --git a/src/confidential.rs b/src/confidential.rs index 9ff4a92..2ecb114 100644 --- a/src/confidential.rs +++ b/src/confidential.rs @@ -2,7 +2,8 @@ use elements::confidential::{Asset, Nonce, Value}; use elements::AssetId; use serde::{Deserialize, Serialize}; -use crate::{GetInfo, HexBytes, Network}; +use crate::utils::{GetInfo, Network}; +use hal::HexBytes; #[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] diff --git a/src/daemon/actions/address.rs b/src/daemon/actions/address.rs index 36cd0f6..55556be 100644 --- a/src/daemon/actions/address.rs +++ b/src/daemon/actions/address.rs @@ -3,7 +3,7 @@ use elements::hashes::Hash; use elements::{Address, WPubkeyHash, WScriptHash}; use thiserror::Error; -use crate::{address::AddressInfo, Network}; +use crate::{address::AddressInfo, utils::Network}; use super::types::AddressCreateRequest; diff --git a/src/daemon/actions/block.rs b/src/daemon/actions/block.rs index b4ed95e..9040988 100644 --- a/src/daemon/actions/block.rs +++ b/src/daemon/actions/block.rs @@ -3,7 +3,7 @@ use elements::{dynafed, Block, BlockExtData, BlockHeader}; use thiserror::Error; use crate::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; -use crate::Network; +use crate::utils::{GetInfo, Network}; use super::types::{BlockCreateRequest, BlockDecodeRequest}; @@ -78,7 +78,7 @@ pub fn decode(req: BlockDecodeRequest) -> Result if req.txids.unwrap_or(false) { let block: Block = deserialize(&raw_block).map_err(BlockError::BlockDeserialize)?; let info = BlockInfo { - header: crate::GetInfo::get_info(&block.header, network), + header: GetInfo::get_info(&block.header, network), txids: Some(block.txdata.iter().map(|t| t.txid()).collect()), transactions: None, raw_transactions: None, @@ -92,7 +92,7 @@ pub fn decode(req: BlockDecodeRequest) -> Result block.header } }; - let info = crate::GetInfo::get_info(&header, network); + let info = GetInfo::get_info(&header, network); serde_json::to_value(&info).map_err(|e| BlockError::Serialize(format!("{}", e))) } } diff --git a/src/daemon/actions/tx.rs b/src/daemon/actions/tx.rs index 72e0978..2a817af 100644 --- a/src/daemon/actions/tx.rs +++ b/src/daemon/actions/tx.rs @@ -20,7 +20,8 @@ use crate::tx::{ AssetIssuanceInfo, InputInfo, InputScriptInfo, InputWitnessInfo, OutputInfo, OutputScriptInfo, OutputWitnessInfo, PeginDataInfo, PegoutDataInfo, TransactionInfo, }; -use crate::{GetInfo, Network}; + +use crate::utils::{GetInfo, Network}; use super::types::{TxCreateRequest, TxDecodeRequest}; diff --git a/src/hal_simplicity.rs b/src/hal_simplicity.rs index 13d2e6e..ebd3139 100644 --- a/src/hal_simplicity.rs +++ b/src/hal_simplicity.rs @@ -35,13 +35,13 @@ impl Program { /// The canonical representation of witnesses is hex, but old versions of simc /// (e.g. every released version, and master, as of 2025-10-25) output base64. pub fn from_str(prog_b64: &str, wit_hex: Option<&str>) -> Result { - let prog_bytes = crate::hex_or_base64(prog_b64).map_err(ParseError::Base64)?; + let prog_bytes = crate::utils::hex_or_base64(prog_b64).map_err(ParseError::Base64)?; let iter = BitIter::new(prog_bytes.iter().copied()); let commit_prog = CommitNode::decode(iter).map_err(ParseError::Decode)?; let redeem_prog = wit_hex .map(|wit_hex| { - let wit_bytes = crate::hex_or_base64(wit_hex).map_err(ParseError::Base64)?; + let wit_bytes = crate::utils::hex_or_base64(wit_hex).map_err(ParseError::Base64)?; let prog_iter = BitIter::new(prog_bytes.into_iter()); let wit_iter = BitIter::new(wit_bytes.into_iter()); RedeemNode::decode(prog_iter, wit_iter).map_err(ParseError::Decode) diff --git a/src/lib.rs b/src/lib.rs index b39cdcf..80f504e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,59 +10,4 @@ pub mod tx; pub mod confidential; -pub use elements::bitcoin; -pub use hal::HexBytes; - -use elements::AddressParams; -use serde::{Deserialize, Serialize}; - -/// Known Elements networks. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)] -#[serde(rename_all = "lowercase")] -pub enum Network { - ElementsRegtest, - Liquid, - LiquidTestnet, -} - -impl Network { - pub fn from_params(params: &'static AddressParams) -> Option { - if *params == AddressParams::ELEMENTS { - Some(Network::ElementsRegtest) - } else if *params == AddressParams::LIQUID_TESTNET { - Some(Network::LiquidTestnet) - } else if *params == AddressParams::LIQUID { - Some(Network::Liquid) - } else { - None - } - } - - pub fn address_params(self) -> &'static AddressParams { - match self { - Network::ElementsRegtest => &AddressParams::ELEMENTS, - Network::Liquid => &AddressParams::LIQUID, - Network::LiquidTestnet => &AddressParams::LIQUID_TESTNET, - } - } -} - -/// Get JSON-able objects that describe the type. -pub trait GetInfo { - /// Get a description of this object given the network of interest. - fn get_info(&self, network: Network) -> T; -} - -/// Parse a string which may be base64 or hex-encoded. -/// -/// An even-length string with exclusively lowercase hex characters will be parsed as hex; -/// failing that, it will be parsed as base64 and return an error accordingly. -pub fn hex_or_base64(s: &str) -> Result, simplicity::base64::DecodeError> { - if s.len() % 2 == 0 && s.bytes().all(|b| b.is_ascii_hexdigit() && b.is_ascii_lowercase()) { - use simplicity::hex::FromHex as _; - Ok(Vec::from_hex(s).expect("charset checked above")) - } else { - use simplicity::base64::prelude::Engine as _; - simplicity::base64::prelude::BASE64_STANDARD.decode(s) - } -} +pub mod utils; diff --git a/src/tx.rs b/src/tx.rs index 50140b4..03a8300 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -7,7 +7,9 @@ use elements::{ use serde::{Deserialize, Serialize}; -use crate::{GetInfo, HexBytes, Network}; +use hal::HexBytes; + +use crate::utils::{GetInfo, Network}; use crate::confidential::{ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialValueInfo}; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..065f72c --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,53 @@ +use elements::AddressParams; +use serde::{Deserialize, Serialize}; + +/// Known Elements networks. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum Network { + ElementsRegtest, + Liquid, + LiquidTestnet, +} + +impl Network { + pub fn from_params(params: &'static AddressParams) -> Option { + if *params == AddressParams::ELEMENTS { + Some(Network::ElementsRegtest) + } else if *params == AddressParams::LIQUID_TESTNET { + Some(Network::LiquidTestnet) + } else if *params == AddressParams::LIQUID { + Some(Network::Liquid) + } else { + None + } + } + + pub fn address_params(self) -> &'static AddressParams { + match self { + Network::ElementsRegtest => &AddressParams::ELEMENTS, + Network::Liquid => &AddressParams::LIQUID, + Network::LiquidTestnet => &AddressParams::LIQUID_TESTNET, + } + } +} + +/// Get JSON-able objects that describe the type. +pub trait GetInfo { + /// Get a description of this object given the network of interest. + fn get_info(&self, network: Network) -> T; +} + +/// Parse a string which may be base64 or hex-encoded. +/// +/// An even-length string with exclusively lowercase hex characters will be parsed as hex; +/// failing that, it will be parsed as base64 and return an error accordingly. +pub fn hex_or_base64(s: &str) -> Result, simplicity::base64::DecodeError> { + if s.len() % 2 == 0 && s.bytes().all(|b| b.is_ascii_hexdigit() && b.is_ascii_lowercase()) { + use simplicity::hex::FromHex as _; + Ok(Vec::from_hex(s).expect("charset checked above")) + } else { + use simplicity::base64::prelude::Engine as _; + simplicity::base64::prelude::BASE64_STANDARD.decode(s) + } +} From 3a05bf53a00aa823ad7c821f6575e86f16db54fb Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 16:18:53 +0200 Subject: [PATCH 06/39] Initialize the cargo workspace --- Cargo.lock | 6 +- Cargo.toml | 48 +- .../hal-simplicity/cmd/address.rs | 0 .../hal-simplicity/cmd/block.rs | 0 .../hal-simplicity/cmd/keypair.rs | 0 .../hal-simplicity/cmd/mod.rs | 0 .../hal-simplicity/cmd/simplicity/info.rs | 0 .../hal-simplicity/cmd/simplicity/mod.rs | 0 .../cmd/simplicity/pset/create.rs | 0 .../cmd/simplicity/pset/extract.rs | 0 .../cmd/simplicity/pset/finalize.rs | 0 .../hal-simplicity/cmd/simplicity/pset/mod.rs | 0 .../hal-simplicity/cmd/simplicity/pset/run.rs | 0 .../cmd/simplicity/pset/update_input.rs | 0 .../hal-simplicity/cmd/simplicity/sighash.rs | 0 .../hal-simplicity/cmd/tx.rs | 0 .../hal-simplicity/main.rs | 0 hal-simplicity-daemon/Cargo.lock | 1333 +++++++++++++++++ hal-simplicity-daemon/Cargo.toml | 40 + .../src}/daemon/actions/address.rs | 11 +- .../src}/daemon/actions/block.rs | 2 +- .../src}/daemon/actions/keypair.rs | 0 .../src}/daemon/actions/mod.rs | 0 .../src}/daemon/actions/simplicity/info.rs | 2 +- .../src}/daemon/actions/simplicity/mod.rs | 0 .../daemon/actions/simplicity/pset/create.rs | 0 .../daemon/actions/simplicity/pset/extract.rs | 0 .../actions/simplicity/pset/finalize.rs | 2 +- .../daemon/actions/simplicity/pset/mod.rs | 0 .../daemon/actions/simplicity/pset/run.rs | 2 +- .../actions/simplicity/pset/update_input.rs | 2 +- .../src}/daemon/actions/simplicity/sighash.rs | 0 .../src}/daemon/actions/tx.rs | 4 +- .../src}/daemon/actions/types.rs | 0 .../src}/daemon/handler.rs | 0 .../src}/daemon/mod.rs | 8 +- .../src}/jsonrpc/mod.rs | 0 hal-simplicity-daemon/src/lib.rs | 7 + .../src/utils}/address.rs | 0 .../src/utils}/block.rs | 4 +- .../src/utils}/confidential.rs | 0 .../src/utils}/hal_simplicity.rs | 0 .../src/utils/mod.rs | 6 + .../src/utils}/tx.rs | 4 +- src/bip32.rs | 21 - src/lib.rs | 13 - 46 files changed, 1411 insertions(+), 104 deletions(-) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/address.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/block.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/keypair.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/mod.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/info.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/mod.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/pset/create.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/pset/extract.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/pset/finalize.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/pset/mod.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/pset/run.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/pset/update_input.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/simplicity/sighash.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/cmd/tx.rs (100%) rename {src/bin => hal-simplicity-client}/hal-simplicity/main.rs (100%) create mode 100644 hal-simplicity-daemon/Cargo.lock create mode 100644 hal-simplicity-daemon/Cargo.toml rename {src => hal-simplicity-daemon/src}/daemon/actions/address.rs (92%) rename {src => hal-simplicity-daemon/src}/daemon/actions/block.rs (98%) rename {src => hal-simplicity-daemon/src}/daemon/actions/keypair.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/mod.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/info.rs (96%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/mod.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/pset/create.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/pset/extract.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/pset/finalize.rs (97%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/pset/mod.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/pset/run.rs (98%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/pset/update_input.rs (99%) rename {src => hal-simplicity-daemon/src}/daemon/actions/simplicity/sighash.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/actions/tx.rs (99%) rename {src => hal-simplicity-daemon/src}/daemon/actions/types.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/handler.rs (100%) rename {src => hal-simplicity-daemon/src}/daemon/mod.rs (95%) rename {src => hal-simplicity-daemon/src}/jsonrpc/mod.rs (100%) create mode 100644 hal-simplicity-daemon/src/lib.rs rename {src => hal-simplicity-daemon/src/utils}/address.rs (100%) rename {src => hal-simplicity-daemon/src/utils}/block.rs (98%) rename {src => hal-simplicity-daemon/src/utils}/confidential.rs (100%) rename {src => hal-simplicity-daemon/src/utils}/hal_simplicity.rs (100%) rename src/utils.rs => hal-simplicity-daemon/src/utils/mod.rs (94%) rename {src => hal-simplicity-daemon/src/utils}/tx.rs (98%) delete mode 100644 src/bip32.rs delete mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8c17826..ef1961c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,9 +175,9 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byteorder" @@ -338,7 +338,7 @@ dependencies = [ ] [[package]] -name = "hal-simplicity" +name = "hal-simplicity-daemon" version = "0.1.0" dependencies = [ "clap", diff --git a/Cargo.toml b/Cargo.toml index 0814ae1..36b2bee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,45 +1,3 @@ -[package] -name = "hal-simplicity" -version = "0.1.0" -edition = "2021" -authors = ["Steven Roose ", "Andrew Poelstra "] -license = "CC0-1.0" -homepage = "https://github.com/BlockstreamResearch/hal-simplicity/" -repository = "https://github.com/BlockstreamResearch/hal-simplicity/" -description = "hal-simplicity: a Simplicity extension of hal" -keywords = [ "crypto", "bitcoin", "elements", "liquid", "simplicity" ] -readme = "README.md" - -[lib] -name = "hal_simplicity" -path = "src/lib.rs" - -[[bin]] -name = "hal-simplicity" -path = "src/bin/hal-simplicity/main.rs" - - -[dependencies] -hal = "0.10.0" -clap = "2.32" -log = "0.4.5" -fern = "0.5.6" - -serde = { version = "1.0.84", features = [ "derive" ] } -serde_json = "1.0.34" -serde_yaml = "0.8.8" -hex = "0.3.2" - -elements = { version = "0.25.2", features = [ "serde", "base64" ] } -simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } -hyper = { version = "1.8.1", features = ["server", "http1"] } -hyper-util = { version = "0.1", features = ["tokio"] } -http-body-util = "0.1" -tokio = { version = "1.48.0", features = ["full"] } -thiserror = "2.0.17" - -[lints.clippy] -# Exclude lints we don't think are valuable. -needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 -manual_range_contains = "allow" # More readable than clippy's format. -uninlined_format_args = "allow" # Stylistic and dumb and inconsistent +[workspace] +resolver = "2" +members = ["hal-simplicity-daemon"] diff --git a/src/bin/hal-simplicity/cmd/address.rs b/hal-simplicity-client/hal-simplicity/cmd/address.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/address.rs rename to hal-simplicity-client/hal-simplicity/cmd/address.rs diff --git a/src/bin/hal-simplicity/cmd/block.rs b/hal-simplicity-client/hal-simplicity/cmd/block.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/block.rs rename to hal-simplicity-client/hal-simplicity/cmd/block.rs diff --git a/src/bin/hal-simplicity/cmd/keypair.rs b/hal-simplicity-client/hal-simplicity/cmd/keypair.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/keypair.rs rename to hal-simplicity-client/hal-simplicity/cmd/keypair.rs diff --git a/src/bin/hal-simplicity/cmd/mod.rs b/hal-simplicity-client/hal-simplicity/cmd/mod.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/mod.rs rename to hal-simplicity-client/hal-simplicity/cmd/mod.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/info.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/info.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/info.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/info.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/mod.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/mod.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/mod.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/mod.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/pset/create.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/create.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/pset/create.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/create.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/pset/extract.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/extract.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/pset/extract.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/extract.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/pset/finalize.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/finalize.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/pset/finalize.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/finalize.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/pset/mod.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/mod.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/pset/mod.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/mod.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/pset/run.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/run.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/pset/run.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/run.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/pset/update_input.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/update_input.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/pset/update_input.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/update_input.rs diff --git a/src/bin/hal-simplicity/cmd/simplicity/sighash.rs b/hal-simplicity-client/hal-simplicity/cmd/simplicity/sighash.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/simplicity/sighash.rs rename to hal-simplicity-client/hal-simplicity/cmd/simplicity/sighash.rs diff --git a/src/bin/hal-simplicity/cmd/tx.rs b/hal-simplicity-client/hal-simplicity/cmd/tx.rs similarity index 100% rename from src/bin/hal-simplicity/cmd/tx.rs rename to hal-simplicity-client/hal-simplicity/cmd/tx.rs diff --git a/src/bin/hal-simplicity/main.rs b/hal-simplicity-client/hal-simplicity/main.rs similarity index 100% rename from src/bin/hal-simplicity/main.rs rename to hal-simplicity-client/hal-simplicity/main.rs diff --git a/hal-simplicity-daemon/Cargo.lock b/hal-simplicity-daemon/Cargo.lock new file mode 100644 index 0000000..8c17826 --- /dev/null +++ b/hal-simplicity-daemon/Cargo.lock @@ -0,0 +1,1333 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals", + "bitcoin_hashes", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64-compat" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin" +version = "0.32.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" +dependencies = [ + "base58ck", + "base64", + "bech32", + "bitcoin-internals", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes", + "hex-conservative", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +dependencies = [ + "bitcoin-io", + "hex-conservative", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "elements" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b2569d3495bfdfce36c504fd4d78752ff4a7699f8a33e6f3ee523bddf9f6ad" +dependencies = [ + "bech32", + "bitcoin", + "secp256k1-zkp", + "serde", + "serde_json", +] + +[[package]] +name = "fern" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" +dependencies = [ + "chrono", + "log", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hal" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f57a34f3cc18f2967a98c61abea03b1b9521075e880b18f45b63561a88c3c174" +dependencies = [ + "base64-compat", + "bip39", + "bitcoin", + "byteorder", + "chrono", + "clap", + "fern", + "hex", + "jobserver", + "lazy_static", + "lightning-invoice", + "log", + "miniscript", + "secp256k1", + "serde", + "serde_json", + "serde_yaml", + "shell-escape", +] + +[[package]] +name = "hal-simplicity" +version = "0.1.0" +dependencies = [ + "clap", + "elements", + "fern", + "hal", + "hex", + "http-body-util", + "hyper", + "hyper-util", + "log", + "serde", + "serde_json", + "serde_yaml", + "simplicity-lang", + "thiserror", + "tokio", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "lightning-invoice" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11209f386879b97198b2bfc9e9c1e5d42870825c6bd4376f17f95357244d6600" +dependencies = [ + "bech32", + "bitcoin", + "lightning-types", +] + +[[package]] +name = "lightning-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cd84d4e71472035903e43caded8ecc123066ce466329ccd5ae537a8d5488c7" +dependencies = [ + "bitcoin", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miniscript" +version = "12.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" +dependencies = [ + "bech32", + "bitcoin", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "santiago" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de36022292bc2086eb8f55bffa460fef3475e4459b478820711f4c421feb87ec" +dependencies = [ + "regex", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes", + "rand", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-zkp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" +dependencies = [ + "bitcoin-private", + "rand", + "secp256k1", + "secp256k1-zkp-sys", + "serde", +] + +[[package]] +name = "secp256k1-zkp-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f08b2d0b143a22e07f798ae4f0ab20d5590d7c68e0d090f2088a48a21d1654" +dependencies = [ + "cc", + "secp256k1-sys", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + +[[package]] +name = "simplicity-lang" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525879699aba1f7f75c0d97355475072adeb0ed0530df4e18f23235252475e68" +dependencies = [ + "bitcoin", + "bitcoin_hashes", + "byteorder", + "elements", + "getrandom 0.2.16", + "hex-conservative", + "miniscript", + "santiago", + "serde", + "simplicity-sys", +] + +[[package]] +name = "simplicity-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abf9c7d64c5bf45bb2fb966f3b0637d8c13c8d5cdfbd7587900421cb7584c49" +dependencies = [ + "bitcoin_hashes", + "cc", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/hal-simplicity-daemon/Cargo.toml b/hal-simplicity-daemon/Cargo.toml new file mode 100644 index 0000000..a77e9d1 --- /dev/null +++ b/hal-simplicity-daemon/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "hal-simplicity-daemon" +version = "0.1.0" +edition = "2021" +authors = ["Steven Roose ", "Andrew Poelstra "] +license = "CC0-1.0" +homepage = "https://github.com/BlockstreamResearch/hal-simplicity/" +repository = "https://github.com/BlockstreamResearch/hal-simplicity/" +description = "hal-simplicity: a Simplicity extension of hal" +keywords = [ "crypto", "bitcoin", "elements", "liquid", "simplicity" ] +readme = "README.md" + +[lib] +name = "hal_simplicity_daemon" +path = "src/lib.rs" + +[dependencies] +hal = "0.10.0" +clap = "2.32" +log = "0.4.5" +fern = "0.5.6" + +serde = { version = "1.0.84", features = [ "derive" ] } +serde_json = "1.0.34" +serde_yaml = "0.8.8" +hex = "0.3.2" + +elements = { version = "0.25.2", features = [ "serde", "base64" ] } +simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } +hyper = { version = "1.8.1", features = ["server", "http1"] } +hyper-util = { version = "0.1", features = ["tokio"] } +http-body-util = "0.1" +tokio = { version = "1.48.0", features = ["full"] } +thiserror = "2.0.17" + +[lints.clippy] +# Exclude lints we don't think are valuable. +needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 +manual_range_contains = "allow" # More readable than clippy's format. +uninlined_format_args = "allow" # Stylistic and dumb and inconsistent diff --git a/src/daemon/actions/address.rs b/hal-simplicity-daemon/src/daemon/actions/address.rs similarity index 92% rename from src/daemon/actions/address.rs rename to hal-simplicity-daemon/src/daemon/actions/address.rs index 55556be..906f8db 100644 --- a/src/daemon/actions/address.rs +++ b/hal-simplicity-daemon/src/daemon/actions/address.rs @@ -3,7 +3,10 @@ use elements::hashes::Hash; use elements::{Address, WPubkeyHash, WScriptHash}; use thiserror::Error; -use crate::{address::AddressInfo, utils::Network}; +use crate::utils::{ + address::{AddressInfo, Addresses}, + Network, +}; use super::types::AddressCreateRequest; @@ -31,7 +34,7 @@ pub enum AddressError { AddressParse(elements::address::AddressError), } -pub fn create(req: AddressCreateRequest) -> Result { +pub fn create(req: AddressCreateRequest) -> Result { let network = req.network.as_deref().map(parse_network).transpose()?.unwrap_or(Network::ElementsRegtest); @@ -45,11 +48,11 @@ pub fn create(req: AddressCreateRequest) -> Result Result<(), DaemonError> { - println!("Listening on http://{}", self.address); - let shutdown_tx = self.shutdown_tx.clone(); let rpc_service = self.rpc_service.clone(); @@ -71,18 +69,14 @@ impl HalSimplicityDaemon { let io = TokioIo::new(stream); let rpc_service_clone = rpc_service.clone(); tokio::task::spawn(async move { - if let Err(err) = http1::Builder::new() + http1::Builder::new() .serve_connection(io, service_fn(move |req| { handle_request(req, rpc_service_clone.clone()) })) .await - { - eprintln!("Connection error: {:?}", err); - } }); } _ = shutdown_rx.recv() => { - println!("Server shutting down..."); break; } } diff --git a/src/jsonrpc/mod.rs b/hal-simplicity-daemon/src/jsonrpc/mod.rs similarity index 100% rename from src/jsonrpc/mod.rs rename to hal-simplicity-daemon/src/jsonrpc/mod.rs diff --git a/hal-simplicity-daemon/src/lib.rs b/hal-simplicity-daemon/src/lib.rs new file mode 100644 index 0000000..87eec8b --- /dev/null +++ b/hal-simplicity-daemon/src/lib.rs @@ -0,0 +1,7 @@ +pub extern crate simplicity; + +pub mod daemon; +pub mod jsonrpc; +pub mod utils; + +pub use daemon::HalSimplicityDaemon; diff --git a/src/address.rs b/hal-simplicity-daemon/src/utils/address.rs similarity index 100% rename from src/address.rs rename to hal-simplicity-daemon/src/utils/address.rs diff --git a/src/block.rs b/hal-simplicity-daemon/src/utils/block.rs similarity index 98% rename from src/block.rs rename to hal-simplicity-daemon/src/utils/block.rs index d850ac9..50614c6 100644 --- a/src/block.rs +++ b/hal-simplicity-daemon/src/utils/block.rs @@ -2,10 +2,10 @@ use elements::hashes::sha256; use elements::{dynafed, Block, BlockExtData, BlockHash, BlockHeader, TxMerkleNode, Txid}; use serde::{Deserialize, Serialize}; -use crate::utils::{GetInfo, Network}; +use super::{GetInfo, Network}; use hal::HexBytes; -use crate::tx::TransactionInfo; +use super::tx::TransactionInfo; #[derive(Clone, Default, PartialEq, Eq, Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] diff --git a/src/confidential.rs b/hal-simplicity-daemon/src/utils/confidential.rs similarity index 100% rename from src/confidential.rs rename to hal-simplicity-daemon/src/utils/confidential.rs diff --git a/src/hal_simplicity.rs b/hal-simplicity-daemon/src/utils/hal_simplicity.rs similarity index 100% rename from src/hal_simplicity.rs rename to hal-simplicity-daemon/src/utils/hal_simplicity.rs diff --git a/src/utils.rs b/hal-simplicity-daemon/src/utils/mod.rs similarity index 94% rename from src/utils.rs rename to hal-simplicity-daemon/src/utils/mod.rs index 065f72c..ec9e2ee 100644 --- a/src/utils.rs +++ b/hal-simplicity-daemon/src/utils/mod.rs @@ -1,3 +1,9 @@ +pub mod address; +pub mod block; +pub mod confidential; +pub mod hal_simplicity; +pub mod tx; + use elements::AddressParams; use serde::{Deserialize, Serialize}; diff --git a/src/tx.rs b/hal-simplicity-daemon/src/utils/tx.rs similarity index 98% rename from src/tx.rs rename to hal-simplicity-daemon/src/utils/tx.rs index 03a8300..a33a575 100644 --- a/src/tx.rs +++ b/hal-simplicity-daemon/src/utils/tx.rs @@ -9,9 +9,9 @@ use serde::{Deserialize, Serialize}; use hal::HexBytes; -use crate::utils::{GetInfo, Network}; +use super::{GetInfo, Network}; -use crate::confidential::{ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialValueInfo}; +use super::confidential::{ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialValueInfo}; const BTCNET: elements::bitcoin::Network = elements::bitcoin::Network::Bitcoin; diff --git a/src/bip32.rs b/src/bip32.rs deleted file mode 100644 index 0b17e84..0000000 --- a/src/bip32.rs +++ /dev/null @@ -1,21 +0,0 @@ -use bitcoin::{Address, Network}; - -#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] -pub struct DerivationInfo { - pub network: Network, - pub master_fingerprint: ::HexBytes, - pub path: String, - pub chain_code: ::HexBytes, - pub identifier: ::HexBytes, - pub fingerprint: ::HexBytes, - pub public_key: ::HexBytes, - #[serde(skip_serializing_if = "Option::is_none")] - pub secret_key: Option, - pub parent_fingerprint: ::HexBytes, - #[serde(rename = "address-p2pkh")] - pub address_p2pkh: Address, - #[serde(rename = "address-p2wpkh")] - pub address_p2wpkh: Address, - #[serde(rename = "address-p2shwpkh")] - pub address_p2shwpkh: Address, -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 80f504e..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub extern crate simplicity; - -pub mod daemon; -pub mod jsonrpc; - -pub mod address; -pub mod block; -pub mod hal_simplicity; -pub mod tx; - -pub mod confidential; - -pub mod utils; From 32df1b52b45a1e5265341169cfc5eea79370316f Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 17:09:23 +0200 Subject: [PATCH 07/39] Add a standalone daemon running binary --- Cargo.lock | 1 + hal-simplicity-daemon/Cargo.toml | 1 + hal-simplicity-daemon/src/daemon/mod.rs | 65 ++++++++++++------ hal-simplicity-daemon/src/main.rs | 89 +++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 hal-simplicity-daemon/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index ef1961c..12f5a63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,6 +341,7 @@ dependencies = [ name = "hal-simplicity-daemon" version = "0.1.0" dependencies = [ + "chrono", "clap", "elements", "fern", diff --git a/hal-simplicity-daemon/Cargo.toml b/hal-simplicity-daemon/Cargo.toml index a77e9d1..ccf1c80 100644 --- a/hal-simplicity-daemon/Cargo.toml +++ b/hal-simplicity-daemon/Cargo.toml @@ -19,6 +19,7 @@ hal = "0.10.0" clap = "2.32" log = "0.4.5" fern = "0.5.6" +chrono = "0.4" serde = { version = "1.0.84", features = [ "derive" ] } serde_json = "1.0.34" diff --git a/hal-simplicity-daemon/src/daemon/mod.rs b/hal-simplicity-daemon/src/daemon/mod.rs index 592b4d9..8a494c8 100644 --- a/hal-simplicity-daemon/src/daemon/mod.rs +++ b/hal-simplicity-daemon/src/daemon/mod.rs @@ -50,43 +50,64 @@ impl HalSimplicityDaemon { }) } + /// Core event loop that accepts connections and handles them + async fn run_event_loop( + listener: TcpListener, + rpc_service: Arc>, + mut shutdown_rx: broadcast::Receiver<()>, + ) -> Result<(), DaemonError> { + loop { + tokio::select! { + Ok((stream, _)) = listener.accept() => { + let io = TokioIo::new(stream); + let rpc_service_clone = rpc_service.clone(); + tokio::task::spawn(async move { + http1::Builder::new() + .serve_connection(io, service_fn(move |req| { + handle_request(req, rpc_service_clone.clone()) + })) + .await + }); + } + _ = shutdown_rx.recv() => { + break; + } + } + } + + Ok(()) + } + /// Start the daemon on a new thread pub fn start(&mut self) -> Result<(), DaemonError> { + let address = self.address; let shutdown_tx = self.shutdown_tx.clone(); let rpc_service = self.rpc_service.clone(); let runtime = tokio::runtime::Runtime::new()?; - - let listener = runtime.block_on(async { TcpListener::bind(&self.address).await })?; + let listener = runtime.block_on(async { TcpListener::bind(&address).await })?; std::thread::spawn(move || { runtime.block_on(async move { - let mut shutdown_rx = shutdown_tx.subscribe(); - - loop { - tokio::select! { - Ok((stream, _)) = listener.accept() => { - let io = TokioIo::new(stream); - let rpc_service_clone = rpc_service.clone(); - tokio::task::spawn(async move { - http1::Builder::new() - .serve_connection(io, service_fn(move |req| { - handle_request(req, rpc_service_clone.clone()) - })) - .await - }); - } - _ = shutdown_rx.recv() => { - break; - } - } - } + let shutdown_rx = shutdown_tx.subscribe(); + let _ = Self::run_event_loop(listener, rpc_service, shutdown_rx).await; }); }); Ok(()) } + /// Start the daemon and block the current thread + pub fn listen_blocking(self) -> Result<(), DaemonError> { + let runtime = tokio::runtime::Runtime::new()?; + + runtime.block_on(async move { + let listener = TcpListener::bind(&self.address).await?; + let shutdown_rx = self.shutdown_tx.subscribe(); + Self::run_event_loop(listener, self.rpc_service, shutdown_rx).await + }) + } + /// Shutdown the daemon pub fn shutdown(&self) { let _ = self.shutdown_tx.send(()); diff --git a/hal-simplicity-daemon/src/main.rs b/hal-simplicity-daemon/src/main.rs new file mode 100644 index 0000000..094cd2c --- /dev/null +++ b/hal-simplicity-daemon/src/main.rs @@ -0,0 +1,89 @@ +use std::panic; +use std::process; + +use hal_simplicity_daemon::HalSimplicityDaemon; + +/// Default address for the TCP listener +const DEFAULT_ADDRESS: &str = "127.0.0.1:28579"; + +/// Setup logging with the given log level. +fn setup_logger(lvl: log::LevelFilter) { + fern::Dispatch::new() + .format(|out, message, _record| out.finish(format_args!("{}", message))) + .level(lvl) + .chain(std::io::stderr()) + .apply() + .expect("error setting up logger"); +} + +/// Create the main app object. +fn init_app<'a, 'b>() -> clap::App<'a, 'b> { + clap::App::new("hal-simplicity-daemon") + .bin_name("hal-simplicity-daemon") + .version(clap::crate_version!()) + .about("hal-simplicity-daemon -- JSON-RPC daemon for Simplicity operations") + .arg( + clap::Arg::with_name("address") + .short("a") + .long("address") + .value_name("ADDRESS") + .help("TCP address to bind to (default: 127.0.0.1:28579)") + .takes_value(true), + ) + .arg( + clap::Arg::with_name("verbose") + .short("v") + .long("verbose") + .help("Enable verbose logging output to stderr") + .takes_value(false), + ) +} + +fn main() { + // Apply a custom panic hook to print a more user-friendly message + // in case the execution fails. + panic::set_hook(Box::new(|info| { + let message = if let Some(m) = info.payload().downcast_ref::() { + m + } else if let Some(m) = info.payload().downcast_ref::<&str>() { + m + } else { + "No error message provided" + }; + + log::error!("Execution failed: {}", message); + + process::exit(1); + })); + + let app = init_app(); + let matches = app.get_matches(); + + // Enable logging in verbose mode. + match matches.is_present("verbose") { + true => setup_logger(log::LevelFilter::Debug), + false => setup_logger(log::LevelFilter::Info), + } + + // Get the address from command line or use default + let address = matches.value_of("address").unwrap_or(DEFAULT_ADDRESS); + + log::info!("Starting hal-simplicity-daemon on {}...", address); + + // Create the daemon + let daemon = match HalSimplicityDaemon::new(address) { + Ok(d) => d, + Err(e) => { + log::error!("Failed to create daemon: {}", e); + + process::exit(1); + } + }; + + // Start the daemon and block + if let Err(e) = daemon.listen_blocking() { + log::error!("Daemon error: {}", e); + + process::exit(1); + } +} From a54381391395bd04ac99137c83091a6fedcbd351 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 17:12:33 +0200 Subject: [PATCH 08/39] Add README for hal-simplicity-daemon with usage instructions --- hal-simplicity-daemon/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 hal-simplicity-daemon/README.md diff --git a/hal-simplicity-daemon/README.md b/hal-simplicity-daemon/README.md new file mode 100644 index 0000000..3bfbcf1 --- /dev/null +++ b/hal-simplicity-daemon/README.md @@ -0,0 +1,17 @@ +# hal-simplicity-daemon + +A JSON-RPC daemon for Simplicity operations. Listens for HTTP POST requests and handles Simplicity-related operations via JSON-RPC. + +## Usage + +Run with default address (`127.0.0.1:8080`): + +```bash +cargo run --bin hal-simplicity-daemon +``` + +Run with custom address: + +```bash +cargo run --bin hal-simplicity-daemon -- -a 0.0.0.0:3000 +``` From 152437a53a67b0cf87854710bcb786c404553c18 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 17:21:25 +0200 Subject: [PATCH 09/39] Move types out of daemon --- hal-simplicity-daemon/src/daemon/actions/address.rs | 2 +- hal-simplicity-daemon/src/daemon/actions/block.rs | 2 +- hal-simplicity-daemon/src/daemon/actions/keypair.rs | 2 +- hal-simplicity-daemon/src/daemon/actions/mod.rs | 2 -- hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs | 2 +- .../src/daemon/actions/simplicity/pset/create.rs | 2 +- .../src/daemon/actions/simplicity/pset/extract.rs | 2 +- .../src/daemon/actions/simplicity/pset/finalize.rs | 2 +- .../src/daemon/actions/simplicity/pset/run.rs | 2 +- .../src/daemon/actions/simplicity/pset/update_input.rs | 2 +- hal-simplicity-daemon/src/daemon/actions/simplicity/sighash.rs | 2 +- hal-simplicity-daemon/src/daemon/actions/tx.rs | 2 +- hal-simplicity-daemon/src/daemon/handler.rs | 3 ++- hal-simplicity-daemon/src/lib.rs | 2 ++ hal-simplicity-daemon/src/{daemon/actions => }/types.rs | 0 15 files changed, 15 insertions(+), 14 deletions(-) rename hal-simplicity-daemon/src/{daemon/actions => }/types.rs (100%) diff --git a/hal-simplicity-daemon/src/daemon/actions/address.rs b/hal-simplicity-daemon/src/daemon/actions/address.rs index 906f8db..12414d4 100644 --- a/hal-simplicity-daemon/src/daemon/actions/address.rs +++ b/hal-simplicity-daemon/src/daemon/actions/address.rs @@ -8,7 +8,7 @@ use crate::utils::{ Network, }; -use super::types::AddressCreateRequest; +use crate::types::AddressCreateRequest; #[derive(Debug, Error)] pub enum AddressError { diff --git a/hal-simplicity-daemon/src/daemon/actions/block.rs b/hal-simplicity-daemon/src/daemon/actions/block.rs index 1cdb735..1d74cb5 100644 --- a/hal-simplicity-daemon/src/daemon/actions/block.rs +++ b/hal-simplicity-daemon/src/daemon/actions/block.rs @@ -5,7 +5,7 @@ use thiserror::Error; use crate::utils::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; use crate::utils::{GetInfo, Network}; -use super::types::{BlockCreateRequest, BlockDecodeRequest}; +use crate::types::{BlockCreateRequest, BlockDecodeRequest}; #[derive(Debug, Error)] pub enum BlockError { diff --git a/hal-simplicity-daemon/src/daemon/actions/keypair.rs b/hal-simplicity-daemon/src/daemon/actions/keypair.rs index ed97410..19843ed 100644 --- a/hal-simplicity-daemon/src/daemon/actions/keypair.rs +++ b/hal-simplicity-daemon/src/daemon/actions/keypair.rs @@ -1,6 +1,6 @@ use elements::bitcoin::secp256k1::{self, rand}; -use super::types::KeypairGenerateResponse; +use crate::types::KeypairGenerateResponse; pub fn generate() -> KeypairGenerateResponse { let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng()); diff --git a/hal-simplicity-daemon/src/daemon/actions/mod.rs b/hal-simplicity-daemon/src/daemon/actions/mod.rs index 028c9b1..781f129 100644 --- a/hal-simplicity-daemon/src/daemon/actions/mod.rs +++ b/hal-simplicity-daemon/src/daemon/actions/mod.rs @@ -1,5 +1,3 @@ -pub mod types; - pub mod address; pub mod block; pub mod keypair; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs index 532c800..799b2eb 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs @@ -1,5 +1,5 @@ -use crate::daemon::actions::types::{RedeemInfo, SimplicityInfoRequest, SimplicityInfoResponse}; use crate::simplicity::jet; +use crate::types::{RedeemInfo, SimplicityInfoRequest, SimplicityInfoResponse}; use crate::utils::hal_simplicity::{elements_address, Program}; use simplicity::hex::parse::FromHex as _; use thiserror::Error; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/create.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/create.rs index f6cc3b9..b857f35 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/create.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/create.rs @@ -1,5 +1,5 @@ use super::PsetError; -use crate::daemon::actions::types::{PsetCreateRequest, PsetCreateResponse}; +use crate::types::{PsetCreateRequest, PsetCreateResponse}; use elements::confidential; use elements::pset::PartiallySignedTransaction; use elements::{Address, AssetId, OutPoint, Transaction, TxIn, TxOut, Txid}; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/extract.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/extract.rs index a61c1eb..4eb29a2 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/extract.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/extract.rs @@ -1,5 +1,5 @@ use super::PsetError; -use crate::daemon::actions::types::{PsetExtractRequest, PsetExtractResponse}; +use crate::types::{PsetExtractRequest, PsetExtractResponse}; use elements::encode::serialize_hex; use thiserror::Error; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/finalize.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/finalize.rs index c02742a..ab517a9 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/finalize.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/finalize.rs @@ -1,6 +1,6 @@ use super::PsetError; -use crate::daemon::actions::types::{PsetFinalizeRequest, PsetFinalizeResponse}; use crate::simplicity::jet; +use crate::types::{PsetFinalizeRequest, PsetFinalizeResponse}; use crate::utils::hal_simplicity::Program; use thiserror::Error; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/run.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/run.rs index 93d1cfa..77d68fb 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/run.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/run.rs @@ -1,8 +1,8 @@ use super::PsetError; -use crate::daemon::actions::types::{JetCall, PsetRunRequest, PsetRunResponse}; use crate::simplicity::bit_machine::{BitMachine, ExecTracker}; use crate::simplicity::jet; use crate::simplicity::{Cmr, Ihr}; +use crate::types::{JetCall, PsetRunRequest, PsetRunResponse}; use crate::utils::hal_simplicity::Program; use thiserror::Error; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/update_input.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/update_input.rs index e07015e..2566524 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/update_input.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/pset/update_input.rs @@ -1,5 +1,5 @@ use super::PsetError; -use crate::daemon::actions::types::{PsetUpdateInputRequest, PsetUpdateInputResponse}; +use crate::types::{PsetUpdateInputRequest, PsetUpdateInputResponse}; use crate::utils::hal_simplicity::taproot_spend_info; use core::str::FromStr; use elements::bitcoin::secp256k1; diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/sighash.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/sighash.rs index 585b60b..4f57774 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/sighash.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/sighash.rs @@ -1,4 +1,3 @@ -use crate::daemon::actions::types::{SimplicitySighashRequest, SimplicitySighashResponse}; use crate::simplicity::bitcoin::secp256k1::{ schnorr, Keypair, Message, Secp256k1, SecretKey, XOnlyPublicKey, }; @@ -7,6 +6,7 @@ use crate::simplicity::elements::hex::FromHex; use crate::simplicity::elements::taproot::ControlBlock; use crate::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; use crate::simplicity::Cmr; +use crate::types::{SimplicitySighashRequest, SimplicitySighashResponse}; use elements::bitcoin::secp256k1; use elements::hashes::Hash as _; use elements::pset::PartiallySignedTransaction; diff --git a/hal-simplicity-daemon/src/daemon/actions/tx.rs b/hal-simplicity-daemon/src/daemon/actions/tx.rs index 7e3c93f..6c2402e 100644 --- a/hal-simplicity-daemon/src/daemon/actions/tx.rs +++ b/hal-simplicity-daemon/src/daemon/actions/tx.rs @@ -23,7 +23,7 @@ use crate::utils::tx::{ use crate::utils::{GetInfo, Network}; -use super::types::{TxCreateRequest, TxDecodeRequest}; +use crate::types::{TxCreateRequest, TxDecodeRequest}; #[derive(Debug, Error)] pub enum TxError { diff --git a/hal-simplicity-daemon/src/daemon/handler.rs b/hal-simplicity-daemon/src/daemon/handler.rs index 5f9451d..4970a35 100644 --- a/hal-simplicity-daemon/src/daemon/handler.rs +++ b/hal-simplicity-daemon/src/daemon/handler.rs @@ -1,7 +1,8 @@ use crate::jsonrpc::{ErrorCode, JsonRpcService, RpcError, RpcHandler}; use serde_json::Value; -use super::actions::{self, types::*}; +use super::actions; +use crate::types::*; /// RPC method names #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/hal-simplicity-daemon/src/lib.rs b/hal-simplicity-daemon/src/lib.rs index 87eec8b..1387427 100644 --- a/hal-simplicity-daemon/src/lib.rs +++ b/hal-simplicity-daemon/src/lib.rs @@ -4,4 +4,6 @@ pub mod daemon; pub mod jsonrpc; pub mod utils; +pub mod types; + pub use daemon::HalSimplicityDaemon; diff --git a/hal-simplicity-daemon/src/daemon/actions/types.rs b/hal-simplicity-daemon/src/types.rs similarity index 100% rename from hal-simplicity-daemon/src/daemon/actions/types.rs rename to hal-simplicity-daemon/src/types.rs From 31db34259c823be80b27e0e8ee286c789b6b8eb4 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 17:32:37 +0200 Subject: [PATCH 10/39] Add hal-simplicity-client to workspace --- Cargo.lock | 23 + Cargo.toml | 5 +- hal-simplicity-client/Cargo.lock | 1333 +++++++++++++++++ hal-simplicity-client/Cargo.toml | 43 + .../{hal-simplicity => src}/cmd/address.rs | 6 +- .../{hal-simplicity => src}/cmd/block.rs | 2 +- .../{hal-simplicity => src}/cmd/keypair.rs | 0 .../{hal-simplicity => src}/cmd/mod.rs | 2 +- .../cmd/simplicity/info.rs | 4 +- .../cmd/simplicity/mod.rs | 8 +- .../cmd/simplicity/pset/create.rs | 0 .../cmd/simplicity/pset/extract.rs | 0 .../cmd/simplicity/pset/finalize.rs | 4 +- .../cmd/simplicity/pset/mod.rs | 6 +- .../cmd/simplicity/pset/run.rs | 8 +- .../cmd/simplicity/pset/update_input.rs | 2 +- .../cmd/simplicity/sighash.rs | 14 +- .../{hal-simplicity => src}/cmd/tx.rs | 6 +- .../{hal-simplicity => src}/main.rs | 2 +- hal-simplicity-daemon/Cargo.toml | 2 +- 20 files changed, 1436 insertions(+), 34 deletions(-) create mode 100644 hal-simplicity-client/Cargo.lock create mode 100644 hal-simplicity-client/Cargo.toml rename hal-simplicity-client/{hal-simplicity => src}/cmd/address.rs (93%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/block.rs (98%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/keypair.rs (100%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/mod.rs (98%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/info.rs (95%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/mod.rs (90%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/pset/create.rs (100%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/pset/extract.rs (100%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/pset/finalize.rs (96%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/pset/mod.rs (95%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/pset/run.rs (95%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/pset/update_input.rs (98%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/simplicity/sighash.rs (95%) rename hal-simplicity-client/{hal-simplicity => src}/cmd/tx.rs (99%) rename hal-simplicity-client/{hal-simplicity => src}/main.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index 12f5a63..f5ca2d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,29 @@ dependencies = [ "shell-escape", ] +[[package]] +name = "hal-simplicity-client" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "elements", + "fern", + "hal", + "hal-simplicity-daemon", + "hex", + "http-body-util", + "hyper", + "hyper-util", + "log", + "serde", + "serde_json", + "serde_yaml", + "simplicity-lang", + "thiserror", + "tokio", +] + [[package]] name = "hal-simplicity-daemon" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 36b2bee..a9e5008 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ [workspace] resolver = "2" -members = ["hal-simplicity-daemon"] +members = [ + "hal-simplicity-daemon", + "hal-simplicity-client" +] diff --git a/hal-simplicity-client/Cargo.lock b/hal-simplicity-client/Cargo.lock new file mode 100644 index 0000000..8c17826 --- /dev/null +++ b/hal-simplicity-client/Cargo.lock @@ -0,0 +1,1333 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals", + "bitcoin_hashes", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64-compat" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin" +version = "0.32.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" +dependencies = [ + "base58ck", + "base64", + "bech32", + "bitcoin-internals", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes", + "hex-conservative", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +dependencies = [ + "bitcoin-io", + "hex-conservative", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "elements" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b2569d3495bfdfce36c504fd4d78752ff4a7699f8a33e6f3ee523bddf9f6ad" +dependencies = [ + "bech32", + "bitcoin", + "secp256k1-zkp", + "serde", + "serde_json", +] + +[[package]] +name = "fern" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" +dependencies = [ + "chrono", + "log", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hal" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f57a34f3cc18f2967a98c61abea03b1b9521075e880b18f45b63561a88c3c174" +dependencies = [ + "base64-compat", + "bip39", + "bitcoin", + "byteorder", + "chrono", + "clap", + "fern", + "hex", + "jobserver", + "lazy_static", + "lightning-invoice", + "log", + "miniscript", + "secp256k1", + "serde", + "serde_json", + "serde_yaml", + "shell-escape", +] + +[[package]] +name = "hal-simplicity" +version = "0.1.0" +dependencies = [ + "clap", + "elements", + "fern", + "hal", + "hex", + "http-body-util", + "hyper", + "hyper-util", + "log", + "serde", + "serde_json", + "serde_yaml", + "simplicity-lang", + "thiserror", + "tokio", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "lightning-invoice" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11209f386879b97198b2bfc9e9c1e5d42870825c6bd4376f17f95357244d6600" +dependencies = [ + "bech32", + "bitcoin", + "lightning-types", +] + +[[package]] +name = "lightning-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cd84d4e71472035903e43caded8ecc123066ce466329ccd5ae537a8d5488c7" +dependencies = [ + "bitcoin", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miniscript" +version = "12.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" +dependencies = [ + "bech32", + "bitcoin", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "santiago" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de36022292bc2086eb8f55bffa460fef3475e4459b478820711f4c421feb87ec" +dependencies = [ + "regex", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes", + "rand", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-zkp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" +dependencies = [ + "bitcoin-private", + "rand", + "secp256k1", + "secp256k1-zkp-sys", + "serde", +] + +[[package]] +name = "secp256k1-zkp-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f08b2d0b143a22e07f798ae4f0ab20d5590d7c68e0d090f2088a48a21d1654" +dependencies = [ + "cc", + "secp256k1-sys", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + +[[package]] +name = "simplicity-lang" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525879699aba1f7f75c0d97355475072adeb0ed0530df4e18f23235252475e68" +dependencies = [ + "bitcoin", + "bitcoin_hashes", + "byteorder", + "elements", + "getrandom 0.2.16", + "hex-conservative", + "miniscript", + "santiago", + "serde", + "simplicity-sys", +] + +[[package]] +name = "simplicity-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abf9c7d64c5bf45bb2fb966f3b0637d8c13c8d5cdfbd7587900421cb7584c49" +dependencies = [ + "bitcoin_hashes", + "cc", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml new file mode 100644 index 0000000..44d5b80 --- /dev/null +++ b/hal-simplicity-client/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "hal-simplicity-client" +version = "0.1.0" +edition = "2021" +authors = ["Steven Roose ", "Andrew Poelstra "] +license = "CC0-1.0" +homepage = "https://github.com/BlockstreamResearch/hal-simplicity/" +repository = "https://github.com/BlockstreamResearch/hal-simplicity/" +description = "hal-simplicity: a Simplicity extension of hal" +keywords = [ "crypto", "bitcoin", "elements", "liquid", "simplicity" ] +readme = "README.md" + +# [lib] +# name = "hal_simplicity_daemon" +# path = "src/lib.rs" + +[dependencies] +hal-simplicity-daemon = { path = "../hal-simplicity-daemon" } + +hal = "0.10.0" +clap = "2.32" +log = "0.4.5" +fern = "0.5.6" +chrono = "0.4" + +serde = { version = "1.0.84", features = [ "derive" ] } +serde_json = "1.0.34" +serde_yaml = "0.8.8" +hex = "0.3.2" + +elements = { version = "0.25.2", features = [ "serde", "base64" ] } +simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } +hyper = { version = "1.8.1", features = ["server", "http1"] } +hyper-util = { version = "0.1", features = ["tokio"] } +http-body-util = "0.1" +tokio = { version = "1.48.0", features = ["full"] } +thiserror = "2.0.17" + +[lints.clippy] +# Exclude lints we don't think are valuable. +needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 +manual_range_contains = "allow" # More readable than clippy's format. +uninlined_format_args = "allow" # Stylistic and dumb and inconsistent diff --git a/hal-simplicity-client/hal-simplicity/cmd/address.rs b/hal-simplicity-client/src/cmd/address.rs similarity index 93% rename from hal-simplicity-client/hal-simplicity/cmd/address.rs rename to hal-simplicity-client/src/cmd/address.rs index accc978..51906ca 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/address.rs +++ b/hal-simplicity-client/src/cmd/address.rs @@ -40,12 +40,12 @@ fn exec_create<'a>(matches: &clap::ArgMatches<'a>) { let created = if let Some(pubkey_hex) = matches.value_of("pubkey") { let pubkey: PublicKey = pubkey_hex.parse().expect("invalid pubkey"); - hal_simplicity::address::Addresses::from_pubkey(&pubkey, blinder, network) + hal_simplicity_daemon::utils::address::Addresses::from_pubkey(&pubkey, blinder, network) } else if let Some(script_hex) = matches.value_of("script") { let script_bytes = hex::decode(script_hex).expect("invalid script hex"); let script = script_bytes.into(); - hal_simplicity::address::Addresses::from_script(&script, blinder, network) + hal_simplicity_daemon::utils::address::Addresses::from_script(&script, blinder, network) } else { panic!("Can't create addresses without a pubkey"); }; @@ -63,7 +63,7 @@ fn exec_inspect<'a>(matches: &clap::ArgMatches<'a>) { let address: Address = address_str.parse().expect("invalid address format"); let script_pk = address.script_pubkey(); - let mut info = hal_simplicity::address::AddressInfo { + let mut info = hal_simplicity_daemon::utils::address::AddressInfo { network: Network::from_params(address.params).expect("addresses always have params"), script_pub_key: hal::tx::OutputScriptInfo { hex: Some(script_pk.to_bytes().into()), diff --git a/hal-simplicity-client/hal-simplicity/cmd/block.rs b/hal-simplicity-client/src/cmd/block.rs similarity index 98% rename from hal-simplicity-client/hal-simplicity/cmd/block.rs rename to hal-simplicity-client/src/cmd/block.rs index e5d39f3..714d84b 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/block.rs +++ b/hal-simplicity-client/src/cmd/block.rs @@ -5,7 +5,7 @@ use elements::{dynafed, Block, BlockExtData, BlockHeader}; use crate::cmd; use crate::cmd::tx::create_transaction; -use hal_simplicity::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; +use hal_simplicity_daemon::utils::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; use log::warn; pub fn subcommand<'a>() -> clap::App<'a, 'a> { diff --git a/hal-simplicity-client/hal-simplicity/cmd/keypair.rs b/hal-simplicity-client/src/cmd/keypair.rs similarity index 100% rename from hal-simplicity-client/hal-simplicity/cmd/keypair.rs rename to hal-simplicity-client/src/cmd/keypair.rs diff --git a/hal-simplicity-client/hal-simplicity/cmd/mod.rs b/hal-simplicity-client/src/cmd/mod.rs similarity index 98% rename from hal-simplicity-client/hal-simplicity/cmd/mod.rs rename to hal-simplicity-client/src/cmd/mod.rs index 78e7a6e..ab1b019 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/mod.rs +++ b/hal-simplicity-client/src/cmd/mod.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::io; use std::io::Read; -use hal_simplicity::utils::Network; +use hal_simplicity_daemon::utils::Network; /// Build a list of all built-in subcommands. pub fn subcommands<'a>() -> Vec> { diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/info.rs b/hal-simplicity-client/src/cmd/simplicity/info.rs similarity index 95% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/info.rs rename to hal-simplicity-client/src/cmd/simplicity/info.rs index 0f82bcf..fc06161 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/info.rs +++ b/hal-simplicity-client/src/cmd/simplicity/info.rs @@ -5,8 +5,8 @@ use crate::cmd; use super::{Error, ErrorExt as _}; -use hal_simplicity::hal_simplicity::{elements_address, Program}; -use hal_simplicity::simplicity::{jet, Amr, Cmr, Ihr}; +use hal_simplicity_daemon::simplicity::{jet, Amr, Cmr, Ihr}; +use hal_simplicity_daemon::utils::hal_simplicity::{elements_address, Program}; use simplicity::hex::parse::FromHex as _; use serde::Serialize; diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/mod.rs b/hal-simplicity-client/src/cmd/simplicity/mod.rs similarity index 90% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/mod.rs rename to hal-simplicity-client/src/cmd/simplicity/mod.rs index f4d373a..2157ae0 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/mod.rs +++ b/hal-simplicity-client/src/cmd/simplicity/mod.rs @@ -6,10 +6,10 @@ mod pset; mod sighash; use crate::cmd; -use hal_simplicity::simplicity::bitcoin::{Amount, Denomination}; -use hal_simplicity::simplicity::elements::confidential; -use hal_simplicity::simplicity::elements::hex::FromHex as _; -use hal_simplicity::simplicity::jet::elements::ElementsUtxo; +use hal_simplicity_daemon::simplicity::bitcoin::{Amount, Denomination}; +use hal_simplicity_daemon::simplicity::elements::confidential; +use hal_simplicity_daemon::simplicity::elements::hex::FromHex as _; +use hal_simplicity_daemon::simplicity::jet::elements::ElementsUtxo; use serde::Serialize; diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/create.rs b/hal-simplicity-client/src/cmd/simplicity/pset/create.rs similarity index 100% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/create.rs rename to hal-simplicity-client/src/cmd/simplicity/pset/create.rs diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/extract.rs b/hal-simplicity-client/src/cmd/simplicity/pset/extract.rs similarity index 100% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/extract.rs rename to hal-simplicity-client/src/cmd/simplicity/pset/extract.rs diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/finalize.rs b/hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs similarity index 96% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/finalize.rs rename to hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs index 53033c2..fee352d 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/finalize.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs @@ -3,8 +3,8 @@ use crate::cmd; -use hal_simplicity::hal_simplicity::Program; -use hal_simplicity::simplicity::jet; +use hal_simplicity_daemon::simplicity::jet; +use hal_simplicity_daemon::utils::hal_simplicity::Program; use super::super::{Error, ErrorExt as _}; use super::UpdatedPset; diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/mod.rs b/hal-simplicity-client/src/cmd/simplicity/pset/mod.rs similarity index 95% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/mod.rs rename to hal-simplicity-client/src/cmd/simplicity/pset/mod.rs index 16d8a21..f51546f 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/mod.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/mod.rs @@ -16,9 +16,9 @@ use elements::hashes::Hash as _; use elements::pset::PartiallySignedTransaction; use elements::taproot::ControlBlock; use elements::Script; -use hal_simplicity::simplicity::elements::Transaction; -use hal_simplicity::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; -use hal_simplicity::simplicity::Cmr; +use hal_simplicity_daemon::simplicity::elements::Transaction; +use hal_simplicity_daemon::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; +use hal_simplicity_daemon::simplicity::Cmr; use serde::Serialize; #[derive(Serialize)] diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/run.rs b/hal-simplicity-client/src/cmd/simplicity/pset/run.rs similarity index 95% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/run.rs rename to hal-simplicity-client/src/cmd/simplicity/pset/run.rs index 4886ef6..1f94657 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/run.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/run.rs @@ -3,10 +3,10 @@ use crate::cmd; -use hal_simplicity::hal_simplicity::Program; -use hal_simplicity::simplicity::bit_machine::{BitMachine, ExecTracker}; -use hal_simplicity::simplicity::jet; -use hal_simplicity::simplicity::{Cmr, Ihr}; +use hal_simplicity_daemon::simplicity::bit_machine::{BitMachine, ExecTracker}; +use hal_simplicity_daemon::simplicity::jet; +use hal_simplicity_daemon::simplicity::{Cmr, Ihr}; +use hal_simplicity_daemon::utils::hal_simplicity::Program; use super::super::{Error, ErrorExt as _}; diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/update_input.rs b/hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs similarity index 98% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/update_input.rs rename to hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs index 1a9ac3e..fa61275 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/pset/update_input.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs @@ -10,7 +10,7 @@ use super::super::{Error, ErrorExt as _}; use super::UpdatedPset; use elements::schnorr::XOnlyPublicKey; -use hal_simplicity::hal_simplicity::taproot_spend_info; +use hal_simplicity_daemon::utils::hal_simplicity::taproot_spend_info; use simplicity::hex::parse::FromHex as _; pub fn cmd<'a>() -> clap::App<'a, 'a> { diff --git a/hal-simplicity-client/hal-simplicity/cmd/simplicity/sighash.rs b/hal-simplicity-client/src/cmd/simplicity/sighash.rs similarity index 95% rename from hal-simplicity-client/hal-simplicity/cmd/simplicity/sighash.rs rename to hal-simplicity-client/src/cmd/simplicity/sighash.rs index 4e8b461..c98b680 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/simplicity/sighash.rs +++ b/hal-simplicity-client/src/cmd/simplicity/sighash.rs @@ -7,16 +7,16 @@ use super::{Error, ErrorExt as _}; use elements::hashes::Hash as _; use elements::pset::PartiallySignedTransaction; -use hal_simplicity::simplicity::bitcoin::secp256k1::{ +use hal_simplicity_daemon::simplicity::bitcoin::secp256k1::{ schnorr, Keypair, Message, Secp256k1, SecretKey, }; -use hal_simplicity::simplicity::elements; -use hal_simplicity::simplicity::elements::hashes::sha256; -use hal_simplicity::simplicity::elements::hex::FromHex; -use hal_simplicity::simplicity::elements::taproot::ControlBlock; +use hal_simplicity_daemon::simplicity::elements; +use hal_simplicity_daemon::simplicity::elements::hashes::sha256; +use hal_simplicity_daemon::simplicity::elements::hex::FromHex; +use hal_simplicity_daemon::simplicity::elements::taproot::ControlBlock; -use hal_simplicity::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; -use hal_simplicity::simplicity::Cmr; +use hal_simplicity_daemon::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; +use hal_simplicity_daemon::simplicity::Cmr; use serde::Serialize; diff --git a/hal-simplicity-client/hal-simplicity/cmd/tx.rs b/hal-simplicity-client/src/cmd/tx.rs similarity index 99% rename from hal-simplicity-client/hal-simplicity/cmd/tx.rs rename to hal-simplicity-client/src/cmd/tx.rs index f6c3f4d..1f679b2 100644 --- a/hal-simplicity-client/hal-simplicity/cmd/tx.rs +++ b/hal-simplicity-client/src/cmd/tx.rs @@ -15,14 +15,14 @@ use elements::{ use log::warn; use crate::cmd; -use hal_simplicity::confidential::{ +use hal_simplicity_daemon::utils::confidential::{ ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialType, ConfidentialValueInfo, }; -use hal_simplicity::tx::{ +use hal_simplicity_daemon::utils::tx::{ AssetIssuanceInfo, InputInfo, InputScriptInfo, InputWitnessInfo, OutputInfo, OutputScriptInfo, OutputWitnessInfo, PeginDataInfo, PegoutDataInfo, TransactionInfo, }; -use hal_simplicity::utils::Network; +use hal_simplicity_daemon::utils::Network; pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("tx", "manipulate transactions") diff --git a/hal-simplicity-client/hal-simplicity/main.rs b/hal-simplicity-client/src/main.rs similarity index 97% rename from hal-simplicity-client/hal-simplicity/main.rs rename to hal-simplicity-client/src/main.rs index f35f231..7c8270e 100644 --- a/hal-simplicity-client/hal-simplicity/main.rs +++ b/hal-simplicity-client/src/main.rs @@ -3,7 +3,7 @@ use std::process; pub use elements::bitcoin; -pub use hal_simplicity::utils::{GetInfo, Network}; +pub use hal_simplicity_daemon::utils::{GetInfo, Network}; pub mod cmd; diff --git a/hal-simplicity-daemon/Cargo.toml b/hal-simplicity-daemon/Cargo.toml index ccf1c80..87db463 100644 --- a/hal-simplicity-daemon/Cargo.toml +++ b/hal-simplicity-daemon/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Steven Roose ", "Andrew Poelstra Date: Wed, 17 Dec 2025 17:45:47 +0200 Subject: [PATCH 11/39] Move the hal simplicity daemon under the daemon feature --- Cargo.lock | 1009 +++++++++++++++++++++++++++++- hal-simplicity-client/Cargo.toml | 7 +- hal-simplicity-client/src/lib.rs | 1 + hal-simplicity-daemon/Cargo.toml | 4 + hal-simplicity-daemon/src/lib.rs | 8 +- 5 files changed, 994 insertions(+), 35 deletions(-) create mode 100644 hal-simplicity-client/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f5ca2d8..e4b1471 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,12 +236,33 @@ dependencies = [ "vec_map", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "elements" version = "0.25.2" @@ -255,6 +276,37 @@ dependencies = [ "serde_json", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fern" version = "0.5.9" @@ -271,6 +323,36 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -286,6 +368,39 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -311,6 +426,25 @@ dependencies = [ "wasip2", ] +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.12.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hal" version = "0.10.1" @@ -349,9 +483,10 @@ dependencies = [ "hal-simplicity-daemon", "hex", "http-body-util", - "hyper", + "hyper 1.8.1", "hyper-util", "log", + "reqwest", "serde", "serde_json", "serde_yaml", @@ -371,7 +506,7 @@ dependencies = [ "hal", "hex", "http-body-util", - "hyper", + "hyper 1.8.1", "hyper-util", "log", "serde", @@ -388,6 +523,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -418,6 +559,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.0" @@ -428,6 +580,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -435,7 +598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -446,8 +609,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -463,6 +626,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.8.1" @@ -473,8 +660,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -484,6 +671,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.19" @@ -492,9 +692,9 @@ checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "bytes", "futures-core", - "http", - "http-body", - "hyper", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", "pin-project-lite", "tokio", ] @@ -523,6 +723,108 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -530,9 +832,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "itoa" version = "1.0.15" @@ -597,6 +915,18 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "lock_api" version = "0.4.14" @@ -618,6 +948,12 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniscript" version = "12.3.5" @@ -639,6 +975,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -654,6 +1007,50 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -677,6 +1074,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -689,6 +1092,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -791,18 +1209,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] -name = "rustversion" -version = "1.0.22" +name = "reqwest" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] [[package]] -name = "ryu" -version = "1.0.20" +name = "rustix" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] name = "santiago" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -811,6 +1291,15 @@ dependencies = [ "regex", ] +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -861,6 +1350,29 @@ dependencies = [ "secp256k1-sys", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.228" @@ -904,13 +1416,25 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "ryu", "serde", "yaml-rust", @@ -965,12 +1489,28 @@ dependencies = [ "cc", ] +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.1" @@ -981,6 +1521,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "strsim" version = "0.8.0" @@ -998,6 +1544,57 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -1027,6 +1624,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -1054,7 +1661,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.1", "tokio-macros", "windows-sys 0.61.2", ] @@ -1070,6 +1677,60 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -1091,12 +1752,45 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1125,6 +1819,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.106" @@ -1157,6 +1864,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1238,13 +1955,31 @@ dependencies = [ "windows-link", ] +[[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 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.5", ] [[package]] @@ -1256,6 +1991,37 @@ dependencies = [ "windows-link", ] +[[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]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + [[package]] name = "windows-targets" version = "0.53.5" @@ -1263,70 +2029,176 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "windows_x86_64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -1336,6 +2208,29 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.31" @@ -1355,3 +2250,57 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index 44d5b80..c25e60c 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -10,9 +10,9 @@ description = "hal-simplicity: a Simplicity extension of hal" keywords = [ "crypto", "bitcoin", "elements", "liquid", "simplicity" ] readme = "README.md" -# [lib] -# name = "hal_simplicity_daemon" -# path = "src/lib.rs" +[lib] +name = "hal_simplicity" +path = "src/lib.rs" [dependencies] hal-simplicity-daemon = { path = "../hal-simplicity-daemon" } @@ -35,6 +35,7 @@ hyper-util = { version = "0.1", features = ["tokio"] } http-body-util = "0.1" tokio = { version = "1.48.0", features = ["full"] } thiserror = "2.0.17" +reqwest = { version = "0.11.27", features = ["blocking"] } [lints.clippy] # Exclude lints we don't think are valuable. diff --git a/hal-simplicity-client/src/lib.rs b/hal-simplicity-client/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/hal-simplicity-client/src/lib.rs @@ -0,0 +1 @@ + diff --git a/hal-simplicity-daemon/Cargo.toml b/hal-simplicity-daemon/Cargo.toml index 87db463..ae2e608 100644 --- a/hal-simplicity-daemon/Cargo.toml +++ b/hal-simplicity-daemon/Cargo.toml @@ -14,6 +14,10 @@ readme = "README.md" name = "hal_simplicity_daemon" path = "src/lib.rs" +[features] +default = ["daemon"] +daemon = [] + [dependencies] hal = "0.10.0" clap = "2.32" diff --git a/hal-simplicity-daemon/src/lib.rs b/hal-simplicity-daemon/src/lib.rs index 1387427..d0de0fd 100644 --- a/hal-simplicity-daemon/src/lib.rs +++ b/hal-simplicity-daemon/src/lib.rs @@ -1,9 +1,13 @@ pub extern crate simplicity; +pub mod types; + +#[cfg(feature = "daemon")] pub mod daemon; +#[cfg(feature = "daemon")] pub mod jsonrpc; +#[cfg(feature = "daemon")] pub mod utils; -pub mod types; - +#[cfg(feature = "daemon")] pub use daemon::HalSimplicityDaemon; From 19094eae25aef83bc14a5db2bacca9f29849e29b Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 17:49:16 +0200 Subject: [PATCH 12/39] Add dependency usage separation based on active features --- hal-simplicity-daemon/Cargo.toml | 49 ++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/hal-simplicity-daemon/Cargo.toml b/hal-simplicity-daemon/Cargo.toml index ae2e608..8fac27d 100644 --- a/hal-simplicity-daemon/Cargo.toml +++ b/hal-simplicity-daemon/Cargo.toml @@ -14,29 +14,48 @@ readme = "README.md" name = "hal_simplicity_daemon" path = "src/lib.rs" +[[bin]] +name = "hal-simplicity-daemon" +path = "src/main.rs" +required-features = ["daemon"] + [features] default = ["daemon"] -daemon = [] +daemon = [ + "dep:hal", + "dep:clap", + "dep:log", + "dep:fern", + "dep:chrono", + "dep:serde_yaml", + "dep:hex", + "dep:hyper", + "dep:hyper-util", + "dep:http-body-util", + "dep:tokio", + "dep:thiserror", +] [dependencies] -hal = "0.10.0" -clap = "2.32" -log = "0.4.5" -fern = "0.5.6" -chrono = "0.4" - +# Core dependencies serde = { version = "1.0.84", features = [ "derive" ] } serde_json = "1.0.34" -serde_yaml = "0.8.8" -hex = "0.3.2" - elements = { version = "0.25.2", features = [ "serde", "base64" ] } simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } -hyper = { version = "1.8.1", features = ["server", "http1"] } -hyper-util = { version = "0.1", features = ["tokio"] } -http-body-util = "0.1" -tokio = { version = "1.48.0", features = ["full"] } -thiserror = "2.0.17" + +# Daemon-only dependencies +hal = { version = "0.10.0", optional = true } +clap = { version = "2.32", optional = true } +log = { version = "0.4.5", optional = true } +fern = { version = "0.5.6", optional = true } +chrono = { version = "0.4", optional = true } +serde_yaml = { version = "0.8.8", optional = true } +hex = { version = "0.3.2", optional = true } +hyper = { version = "1.8.1", features = ["server", "http1"], optional = true } +hyper-util = { version = "0.1", features = ["tokio"], optional = true } +http-body-util = { version = "0.1", optional = true } +tokio = { version = "1.48.0", features = ["full"], optional = true } +thiserror = { version = "2.0.17", optional = true } [lints.clippy] # Exclude lints we don't think are valuable. From 1559db25da4314ab8b0ea875de90b861417d2b5e Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 17:56:18 +0200 Subject: [PATCH 13/39] Move jsonrpc module out of the daemon feature --- hal-simplicity-daemon/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hal-simplicity-daemon/src/lib.rs b/hal-simplicity-daemon/src/lib.rs index d0de0fd..d173782 100644 --- a/hal-simplicity-daemon/src/lib.rs +++ b/hal-simplicity-daemon/src/lib.rs @@ -1,12 +1,11 @@ pub extern crate simplicity; +pub mod jsonrpc; pub mod types; #[cfg(feature = "daemon")] pub mod daemon; #[cfg(feature = "daemon")] -pub mod jsonrpc; -#[cfg(feature = "daemon")] pub mod utils; #[cfg(feature = "daemon")] From 7219e4f406c6a5880b0196bb70edd71c4f9cfa80 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 18:41:17 +0200 Subject: [PATCH 14/39] update the reqwest crate --- Cargo.lock | 644 +++++++++++-------------------- hal-simplicity-client/Cargo.toml | 2 +- 2 files changed, 228 insertions(+), 418 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4b1471..a4f094f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64-compat" version = "1.0.0" @@ -107,7 +113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" dependencies = [ "base58ck", - "base64", + "base64 0.21.7", "bech32", "bitcoin-internals", "bitcoin-io", @@ -252,17 +258,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "elements" version = "0.25.2" @@ -298,7 +293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -360,6 +355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -394,6 +390,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -428,17 +425,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.27" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.12.1", + "http", + "indexmap 2.0.0", "slab", "tokio", "tokio-util", @@ -483,7 +480,7 @@ dependencies = [ "hal-simplicity-daemon", "hex", "http-body-util", - "hyper 1.8.1", + "hyper", "hyper-util", "log", "reqwest", @@ -506,7 +503,7 @@ dependencies = [ "hal", "hex", "http-body-util", - "hyper 1.8.1", + "hyper", "hyper-util", "log", "serde", @@ -525,9 +522,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hermit-abi" @@ -559,17 +556,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.4.0" @@ -580,17 +566,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -598,7 +573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http", ] [[package]] @@ -609,8 +584,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -628,60 +603,57 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.32" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", "futures-core", - "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.6", + "http", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.10", + "pin-utils", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] -name = "hyper" -version = "1.8.1" +name = "hyper-rustls" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http 1.4.0", - "http-body 1.0.1", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", "tokio", + "tokio-rustls", + "tower-service", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.32", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -690,13 +662,24 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ + "base64 0.22.1", "bytes", + "futures-channel", "futures-core", - "http 1.4.0", - "http-body 1.0.1", - "hyper 1.8.1", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2", + "system-configuration", "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] @@ -723,106 +706,14 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - [[package]] name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ - "icu_normalizer", - "icu_properties", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -837,12 +728,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.14.5", ] [[package]] @@ -851,6 +742,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itoa" version = "1.0.15" @@ -921,12 +822,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - [[package]] name = "lock_api" version = "0.4.14" @@ -977,9 +872,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1098,15 +993,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1210,42 +1096,58 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", "hyper-tls", - "ipnet", + "hyper-util", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] @@ -1258,16 +1160,40 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ - "base64", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -1501,16 +1427,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.1" @@ -1521,18 +1437,18 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.111" @@ -1546,37 +1462,29 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "synstructure" -version = "0.13.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "proc-macro2", - "quote", - "syn", + "futures-core", ] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.10.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -1592,7 +1500,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1624,16 +1532,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" version = "1.10.0" @@ -1661,7 +1559,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -1687,6 +1585,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -1700,6 +1608,45 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -1731,6 +1678,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -1752,24 +1705,23 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" -version = "2.5.7" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "vcpkg" version = "0.2.15" @@ -1937,6 +1889,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -1955,15 +1918,6 @@ dependencies = [ "windows-link", ] -[[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" @@ -1991,21 +1945,6 @@ dependencies = [ "windows-link", ] -[[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]] name = "windows-targets" version = "0.52.6" @@ -2039,12 +1978,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[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.6" @@ -2057,12 +1990,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[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.6" @@ -2075,12 +2002,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[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.6" @@ -2105,12 +2026,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[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.6" @@ -2123,12 +2038,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[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.6" @@ -2141,12 +2050,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[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.6" @@ -2159,12 +2062,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[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.6" @@ -2177,28 +2074,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - [[package]] name = "yaml-rust" version = "0.4.5" @@ -2208,29 +2089,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.8.31" @@ -2252,55 +2110,7 @@ dependencies = [ ] [[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" +name = "zeroize" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index c25e60c..045166d 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -35,7 +35,7 @@ hyper-util = { version = "0.1", features = ["tokio"] } http-body-util = "0.1" tokio = { version = "1.48.0", features = ["full"] } thiserror = "2.0.17" -reqwest = { version = "0.11.27", features = ["blocking"] } +reqwest = { version = "0.12.26", features = ["blocking"] } [lints.clippy] # Exclude lints we don't think are valuable. From d53eb728f6426ab86256006d4bb93f990bd3a9f8 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 19:39:55 +0200 Subject: [PATCH 15/39] Add the basic hal simplicity client over JSONRPC --- .../src/hal_simplicity_client.rs | 353 ++++++++++++++++++ hal-simplicity-client/src/lib.rs | 2 + 2 files changed, 355 insertions(+) create mode 100644 hal-simplicity-client/src/hal_simplicity_client.rs diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs new file mode 100644 index 0000000..95a8ed4 --- /dev/null +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -0,0 +1,353 @@ +use hal_simplicity_daemon::jsonrpc::{RpcError, RpcRequest, RpcResponse}; +use hal_simplicity_daemon::types::*; +use reqwest::blocking::Client; +use serde::de::DeserializeOwned; +use serde_json::Value; +use std::time::Duration; +use thiserror::Error; + +/// JSON-RPC client errors +#[derive(Debug, Error)] +pub enum ClientError { + #[error("HTTP request failed: {0}")] + Http(#[from] reqwest::Error), + + #[error("RPC error: {0}")] + Rpc(#[from] RpcError), + + #[error("Serialization error: {0}")] + Serialization(#[from] serde_json::Error), + + #[error("Invalid response: {0}")] + InvalidResponse(String), + + #[error("Connection refused: daemon not running at {0}")] + ConnectionRefused(String), +} + +/// HAL Simplicity client for hal-simplicity-daemon +pub struct HalSimplicity { + client: Client, + url: String, + next_id: std::sync::atomic::AtomicU64, +} + +impl HalSimplicity { + /// Create a new JSON-RPC client + pub fn new(url: String) -> Result { + let client = Client::builder().timeout(Duration::from_secs(30)).build()?; + + Ok(Self { + client, + url, + next_id: std::sync::atomic::AtomicU64::new(1), + }) + } + + /// Create a client with default URL (http://localhost:28579) + pub fn default() -> Result { + Self::new("http://localhost:28579".to_string()) + } + + /// Get next request ID + fn next_id(&self) -> u64 { + self.next_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + } + + /// Send a JSON-RPC request and return the response + fn call( + &self, + method: &str, + params: Option, + ) -> Result { + let id = self.next_id(); + let request = RpcRequest::new(method.to_string(), params, Some(Value::from(id))); + + let json_request = serde_json::to_string(&request)?; + + let response = self + .client + .post(&self.url) + .header("Content-Type", "application/json") + .body(json_request) + .send() + .map_err(|e| { + if e.is_connect() { + ClientError::ConnectionRefused(self.url.clone()) + } else { + ClientError::Http(e) + } + })?; + + let status = response.status(); + let body = response.text()?; + + if !status.is_success() { + return Err(ClientError::InvalidResponse(format!("HTTP {}: {}", status, body))); + } + + let rpc_response: RpcResponse = serde_json::from_str(&body)?; + + if let Some(error) = rpc_response.error { + return Err(ClientError::Rpc(error)); + } + + let result = rpc_response.result.ok_or_else(|| { + ClientError::InvalidResponse("Response missing both result and error".to_string()) + })?; + + Ok(serde_json::from_value(result)?) + } + + /// Check if the daemon is reachable + pub fn ping(&self) -> Result<(), ClientError> { + // Try to generate a keypair as a ping (lightweight operation) + // We could use any RPC method, but keypair_generate is simple + let _result = self.keypair_generate()?; + Ok(()) + } + + /// Get the daemon URL + pub fn url(&self) -> &str { + &self.url + } + + /// Set a custom timeout for requests + pub fn with_timeout(mut self, timeout: Duration) -> Result { + self.client = Client::builder().timeout(timeout).build()?; + Ok(self) + } + + // Address methods + + /// Create an Elements address + pub fn address_create( + &self, + network: Option, + pubkey: Option, + script: Option, + blinder: Option, + ) -> Result { + let params = AddressCreateRequest { + network, + pubkey, + script, + blinder, + }; + let params_value = serde_json::to_value(params)?; + self.call("address_create", Some(params_value)) + } + + /// Inspect an Elements address + pub fn address_inspect(&self, address: String) -> Result { + let params = AddressInspectRequest { + address, + }; + let params_value = serde_json::to_value(params)?; + self.call("address_inspect", Some(params_value)) + } + + // Block methods + + /// Create a block from JSON data + pub fn block_create( + &self, + block_info: String, + raw_stdout: Option, + ) -> Result { + let params = BlockCreateRequest { + block_info, + raw_stdout, + }; + let params_value = serde_json::to_value(params)?; + self.call("block_create", Some(params_value)) + } + + /// Decode a raw block + pub fn block_decode( + &self, + raw_block: String, + network: Option, + txids: Option, + ) -> Result { + let params = BlockDecodeRequest { + raw_block, + network, + txids, + }; + let params_value = serde_json::to_value(params)?; + self.call("block_decode", Some(params_value)) + } + + // Transaction methods + + /// Create a transaction from JSON data + pub fn tx_create( + &self, + tx_info: String, + raw_stdout: Option, + ) -> Result { + let params = TxCreateRequest { + tx_info, + raw_stdout, + }; + let params_value = serde_json::to_value(params)?; + self.call("tx_create", Some(params_value)) + } + + /// Decode a raw transaction + pub fn tx_decode(&self, raw_tx: String, network: Option) -> Result { + let params = TxDecodeRequest { + raw_tx, + network, + }; + let params_value = serde_json::to_value(params)?; + self.call("tx_decode", Some(params_value)) + } + + // Keypair methods + + /// Generate a new keypair + pub fn keypair_generate(&self) -> Result { + let params = KeypairGenerateRequest {}; + let params_value = serde_json::to_value(params)?; + self.call("keypair_generate", Some(params_value)) + } + + // Simplicity methods + + /// Get information about a Simplicity program + pub fn simplicity_info( + &self, + program: String, + witness: Option, + state: Option, + network: Option, + ) -> Result { + let params = SimplicityInfoRequest { + program, + witness, + state, + network, + }; + let params_value = serde_json::to_value(params)?; + self.call("simplicity_info", Some(params_value)) + } + + /// Compute and optionally sign a Simplicity sighash + pub fn simplicity_sighash( + &self, + tx: String, + input_index: u32, + cmr: String, + control_block: Option, + genesis_hash: Option, + secret_key: Option, + public_key: Option, + signature: Option, + input_utxos: Option>, + ) -> Result { + let params = SimplicitySighashRequest { + tx, + input_index, + cmr, + control_block, + genesis_hash, + secret_key, + public_key, + signature, + input_utxos, + }; + let params_value = serde_json::to_value(params)?; + self.call("simplicity_sighash", Some(params_value)) + } + + // PSET methods + + /// Create a new PSET + pub fn pset_create( + &self, + inputs: String, + outputs: String, + network: Option, + ) -> Result { + let params = PsetCreateRequest { + inputs, + outputs, + network, + }; + let params_value = serde_json::to_value(params)?; + self.call("pset_create", Some(params_value)) + } + + /// Extract a transaction from a PSET + pub fn pset_extract(&self, pset: String) -> Result { + let params = PsetExtractRequest { + pset, + }; + let params_value = serde_json::to_value(params)?; + self.call("pset_extract", Some(params_value)) + } + + /// Finalize a PSET input with a Simplicity program + pub fn pset_finalize( + &self, + pset: String, + input_index: u32, + program: String, + witness: String, + genesis_hash: Option, + ) -> Result { + let params = PsetFinalizeRequest { + pset, + input_index, + program, + witness, + genesis_hash, + }; + let params_value = serde_json::to_value(params)?; + self.call("pset_finalize", Some(params_value)) + } + + /// Run a Simplicity program in a PSET context + pub fn pset_run( + &self, + pset: String, + input_index: u32, + program: String, + witness: String, + genesis_hash: Option, + ) -> Result { + let params = PsetRunRequest { + pset, + input_index, + program, + witness, + genesis_hash, + }; + let params_value = serde_json::to_value(params)?; + self.call("pset_run", Some(params_value)) + } + + /// Update a PSET input with additional information + pub fn pset_update_input( + &self, + pset: String, + input_index: u32, + input_utxo: String, + internal_key: Option, + cmr: Option, + state: Option, + ) -> Result { + let params = PsetUpdateInputRequest { + pset, + input_index, + input_utxo, + internal_key, + cmr, + state, + }; + let params_value = serde_json::to_value(params)?; + self.call("pset_update_input", Some(params_value)) + } +} diff --git a/hal-simplicity-client/src/lib.rs b/hal-simplicity-client/src/lib.rs index 8b13789..4a233ca 100644 --- a/hal-simplicity-client/src/lib.rs +++ b/hal-simplicity-client/src/lib.rs @@ -1 +1,3 @@ +pub extern crate simplicity; +pub mod hal_simplicity_client; From 64a4d2aa634d6403611ce69c10933eee42eba2d4 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Wed, 17 Dec 2025 19:41:44 +0200 Subject: [PATCH 16/39] Move the default daemon url to constants --- hal-simplicity-client/src/hal_simplicity_client.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs index 95a8ed4..9c5b198 100644 --- a/hal-simplicity-client/src/hal_simplicity_client.rs +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -6,6 +6,8 @@ use serde_json::Value; use std::time::Duration; use thiserror::Error; +const DEFAULT_DAEMON_URL: &str = "http://localhost:28579"; + /// JSON-RPC client errors #[derive(Debug, Error)] pub enum ClientError { @@ -46,7 +48,7 @@ impl HalSimplicity { /// Create a client with default URL (http://localhost:28579) pub fn default() -> Result { - Self::new("http://localhost:28579".to_string()) + Self::new(DEFAULT_DAEMON_URL.to_string()) } /// Get next request ID @@ -99,14 +101,6 @@ impl HalSimplicity { Ok(serde_json::from_value(result)?) } - /// Check if the daemon is reachable - pub fn ping(&self) -> Result<(), ClientError> { - // Try to generate a keypair as a ping (lightweight operation) - // We could use any RPC method, but keypair_generate is simple - let _result = self.keypair_generate()?; - Ok(()) - } - /// Get the daemon URL pub fn url(&self) -> &str { &self.url From ee598bc0379d1ed9b50e176cbc8b477813894277 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Thu, 18 Dec 2025 12:43:03 +0200 Subject: [PATCH 17/39] Add a feature to embed daemon into a client --- Cargo.lock | 6 +-- hal-simplicity-client/Cargo.toml | 6 ++- .../src/hal_simplicity_client.rs | 38 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4f094f..0433566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,7 +293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1160,7 +1160,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1500,7 +1500,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index 045166d..b7620c2 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -14,8 +14,12 @@ readme = "README.md" name = "hal_simplicity" path = "src/lib.rs" +[features] +default = ["embed_daemon"] +embed_daemon = ["hal-simplicity-daemon/daemon"] + [dependencies] -hal-simplicity-daemon = { path = "../hal-simplicity-daemon" } +hal-simplicity-daemon = { path = "../hal-simplicity-daemon", default-features = false } hal = "0.10.0" clap = "2.32" diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs index 9c5b198..ea22b5e 100644 --- a/hal-simplicity-client/src/hal_simplicity_client.rs +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -6,6 +6,10 @@ use serde_json::Value; use std::time::Duration; use thiserror::Error; +#[cfg(feature = "embed_daemon")] +use hal_simplicity_daemon::HalSimplicityDaemon; + +const DEFAULT_DAEMON_ADDRESS: &str = "127.0.0.1:28579"; const DEFAULT_DAEMON_URL: &str = "http://localhost:28579"; /// JSON-RPC client errors @@ -25,6 +29,10 @@ pub enum ClientError { #[error("Connection refused: daemon not running at {0}")] ConnectionRefused(String), + + #[cfg(feature = "embed_daemon")] + #[error("Daemon error: {0}")] + Daemon(#[from] hal_simplicity_daemon::daemon::DaemonError), } /// HAL Simplicity client for hal-simplicity-daemon @@ -32,6 +40,8 @@ pub struct HalSimplicity { client: Client, url: String, next_id: std::sync::atomic::AtomicU64, + #[cfg(feature = "embed_daemon")] + _daemon: Option, } impl HalSimplicity { @@ -43,14 +53,42 @@ impl HalSimplicity { client, url, next_id: std::sync::atomic::AtomicU64::new(1), + #[cfg(feature = "embed_daemon")] + _daemon: None, }) } /// Create a client with default URL (http://localhost:28579) + #[cfg(not(feature = "embed_daemon"))] pub fn default() -> Result { Self::new(DEFAULT_DAEMON_URL.to_string()) } + /// Create a client with embedded daemon (auto-started) + #[cfg(feature = "embed_daemon")] + pub fn default() -> Result { + Self::with_embedded_daemon() + } + + /// Create a client with an embedded daemon that auto-starts + #[cfg(feature = "embed_daemon")] + pub fn with_embedded_daemon() -> Result { + let mut daemon = HalSimplicityDaemon::new(DEFAULT_DAEMON_ADDRESS)?; + daemon.start()?; + + // Give the daemon a moment to start, crashea + std::thread::sleep(Duration::from_millis(100)); + + let client = Client::builder().timeout(Duration::from_secs(30)).build()?; + + Ok(Self { + client, + url: DEFAULT_DAEMON_URL.to_string(), + next_id: std::sync::atomic::AtomicU64::new(1), + _daemon: Some(daemon), + }) + } + /// Get next request ID fn next_id(&self) -> u64 { self.next_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) From fc001b38d0b23414ae5a7e43cfb6e1c50f5b28d3 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Thu, 18 Dec 2025 19:31:08 +0200 Subject: [PATCH 18/39] update comments in client to keep consistency --- hal-simplicity-client/src/hal_simplicity_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs index ea22b5e..68a5c7a 100644 --- a/hal-simplicity-client/src/hal_simplicity_client.rs +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -64,7 +64,7 @@ impl HalSimplicity { Self::new(DEFAULT_DAEMON_URL.to_string()) } - /// Create a client with embedded daemon (auto-started) + /// Create a client with an embedded daemon that auto-starts #[cfg(feature = "embed_daemon")] pub fn default() -> Result { Self::with_embedded_daemon() From 362d9f969c90c65d532d923267f58ca7ae756908 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 11:56:45 +0200 Subject: [PATCH 19/39] Update CLi commands to use JSONRPC --- hal-simplicity-client/src/cmd/address.rs | 112 +---- hal-simplicity-client/src/cmd/block.rs | 144 ++---- hal-simplicity-client/src/cmd/keypair.rs | 28 +- .../src/cmd/simplicity/info.rs | 105 +--- .../src/cmd/simplicity/mod.rs | 62 +-- .../src/cmd/simplicity/pset/create.rs | 167 +------ .../src/cmd/simplicity/pset/extract.rs | 26 +- .../src/cmd/simplicity/pset/finalize.rs | 72 +-- .../src/cmd/simplicity/pset/mod.rs | 107 +---- .../src/cmd/simplicity/pset/run.rs | 140 +----- .../src/cmd/simplicity/pset/update_input.rs | 129 +---- .../src/cmd/simplicity/sighash.rs | 218 +-------- hal-simplicity-client/src/cmd/tx.rs | 452 +----------------- hal-simplicity-client/src/lib.rs | 2 + hal-simplicity-client/src/main.rs | 29 +- 15 files changed, 241 insertions(+), 1552 deletions(-) diff --git a/hal-simplicity-client/src/cmd/address.rs b/hal-simplicity-client/src/cmd/address.rs index 51906ca..6ca2c2c 100644 --- a/hal-simplicity-client/src/cmd/address.rs +++ b/hal-simplicity-client/src/cmd/address.rs @@ -1,11 +1,8 @@ use clap; -use elements::bitcoin::{secp256k1, PublicKey}; -use elements::hashes::Hash; -use elements::{Address, WPubkeyHash, WScriptHash}; use crate::cmd; - use crate::Network; +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("address", "work with addresses") @@ -13,10 +10,10 @@ pub fn subcommand<'a>() -> clap::App<'a, 'a> { .subcommand(cmd_inspect()) } -pub fn execute<'a>(matches: &clap::ArgMatches<'a>) { +pub fn execute<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { match matches.subcommand() { - ("create", Some(m)) => exec_create(m), - ("inspect", Some(m)) => exec_inspect(m), + ("create", Some(m)) => exec_create(m, client), + ("inspect", Some(m)) => exec_inspect(m, client), (_, _) => unreachable!("clap prints help"), }; } @@ -30,27 +27,28 @@ fn cmd_create<'a>() -> clap::App<'a, 'a> { ]) } -fn exec_create<'a>(matches: &clap::ArgMatches<'a>) { +fn exec_create<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { let network = cmd::network(matches); - let blinder = matches.value_of("blinder").map(|b| { - let bytes = hex::decode(b).expect("invaid blinder hex"); - secp256k1::PublicKey::from_slice(&bytes).expect("invalid blinder") - }); + let network_str = match network { + Network::ElementsRegtest => Some("elementsregtest".to_string()), + Network::Liquid => Some("liquid".to_string()), + Network::LiquidTestnet => Some("liquidtestnet".to_string()), + }; - let created = if let Some(pubkey_hex) = matches.value_of("pubkey") { - let pubkey: PublicKey = pubkey_hex.parse().expect("invalid pubkey"); - hal_simplicity_daemon::utils::address::Addresses::from_pubkey(&pubkey, blinder, network) - } else if let Some(script_hex) = matches.value_of("script") { - let script_bytes = hex::decode(script_hex).expect("invalid script hex"); - let script = script_bytes.into(); + let pubkey = matches.value_of("pubkey").map(|s| s.to_string()); + let script = matches.value_of("script").map(|s| s.to_string()); + let blinder = matches.value_of("blinder").map(|s| s.to_string()); - hal_simplicity_daemon::utils::address::Addresses::from_script(&script, blinder, network) - } else { - panic!("Can't create addresses without a pubkey"); - }; + if pubkey.is_none() && script.is_none() { + panic!("Can't create addresses without a pubkey or script"); + } + + let result = client + .address_create(network_str, pubkey, script, blinder) + .expect("failed to create address"); - cmd::print_output(matches, &created) + cmd::print_output(matches, &result); } fn cmd_inspect<'a>() -> clap::App<'a, 'a> { @@ -58,71 +56,11 @@ fn cmd_inspect<'a>() -> clap::App<'a, 'a> { .args(&[cmd::opt_yaml(), cmd::arg("address", "the address").required(true)]) } -fn exec_inspect<'a>(matches: &clap::ArgMatches<'a>) { +fn exec_inspect<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { let address_str = matches.value_of("address").expect("no address provided"); - let address: Address = address_str.parse().expect("invalid address format"); - let script_pk = address.script_pubkey(); - let mut info = hal_simplicity_daemon::utils::address::AddressInfo { - network: Network::from_params(address.params).expect("addresses always have params"), - script_pub_key: hal::tx::OutputScriptInfo { - hex: Some(script_pk.to_bytes().into()), - asm: Some(script_pk.asm()), - address: None, - type_: None, - }, - type_: None, - pubkey_hash: None, - script_hash: None, - witness_pubkey_hash: None, - witness_script_hash: None, - witness_program_version: None, - blinding_pubkey: address.blinding_pubkey, - unconfidential: if address.blinding_pubkey.is_some() { - Some(Address { - params: address.params, - payload: address.payload.clone(), - blinding_pubkey: None, - }) - } else { - None - }, - }; - - use elements::address::Payload; - match address.payload { - Payload::PubkeyHash(pkh) => { - info.type_ = Some("p2pkh".to_owned()); - info.pubkey_hash = Some(pkh); - } - Payload::ScriptHash(sh) => { - info.type_ = Some("p2sh".to_owned()); - info.script_hash = Some(sh); - } - Payload::WitnessProgram { - version, - program, - } => { - let version = version.to_u8() as usize; - info.witness_program_version = Some(version); - - if version == 0 { - if program.len() == 20 { - info.type_ = Some("p2wpkh".to_owned()); - info.witness_pubkey_hash = - Some(WPubkeyHash::from_slice(&program).expect("size 20")); - } else if program.len() == 32 { - info.type_ = Some("p2wsh".to_owned()); - info.witness_script_hash = - Some(WScriptHash::from_slice(&program).expect("size 32")); - } else { - info.type_ = Some("invalid-witness-program".to_owned()); - } - } else { - info.type_ = Some("unknown-witness-program-version".to_owned()); - } - } - } + let result = + client.address_inspect(address_str.to_string()).expect("failed to inspect address"); - cmd::print_output(matches, &info) + cmd::print_output(matches, &result); } diff --git a/hal-simplicity-client/src/cmd/block.rs b/hal-simplicity-client/src/cmd/block.rs index 714d84b..b87ad12 100644 --- a/hal-simplicity-client/src/cmd/block.rs +++ b/hal-simplicity-client/src/cmd/block.rs @@ -1,12 +1,8 @@ use std::io::Write; -use elements::encode::{deserialize, serialize}; -use elements::{dynafed, Block, BlockExtData, BlockHeader}; - use crate::cmd; -use crate::cmd::tx::create_transaction; -use hal_simplicity_daemon::utils::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; -use log::warn; +use crate::Network; +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("block", "manipulate blocks") @@ -14,10 +10,10 @@ pub fn subcommand<'a>() -> clap::App<'a, 'a> { .subcommand(cmd_decode()) } -pub fn execute<'a>(matches: &clap::ArgMatches<'a>) { +pub fn execute<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { match matches.subcommand() { - ("create", Some(m)) => exec_create(m), - ("decode", Some(m)) => exec_decode(m), + ("create", Some(m)) => exec_create(m, client), + ("decode", Some(m)) => exec_decode(m, client), (_, _) => unreachable!("clap prints help"), }; } @@ -31,91 +27,19 @@ fn cmd_create<'a>() -> clap::App<'a, 'a> { ]) } -fn create_params(info: ParamsInfo) -> dynafed::Params { - match info.params_type { - ParamsType::Null => dynafed::Params::Null, - ParamsType::Compact => dynafed::Params::Compact { - signblockscript: info - .signblockscript - .expect("signblockscript missing in compact params") - .0 - .into(), - signblock_witness_limit: info - .signblock_witness_limit - .expect("signblock_witness_limit missing in compact params"), - elided_root: info.elided_root.expect("elided_root missing in compact params"), - }, - ParamsType::Full => dynafed::Params::Full(dynafed::FullParams::new( - info.signblockscript.expect("signblockscript missing in full params").0.into(), - info.signblock_witness_limit.expect("signblock_witness_limit missing in full params"), - info.fedpeg_program.expect("fedpeg_program missing in full params").0.into(), - info.fedpeg_script.expect("fedpeg_script missing in full params").0, - info.extension_space - .expect("extension space missing in full params") - .into_iter() - .map(|b| b.0) - .collect(), - )), - } -} - -fn create_block_header(info: BlockHeaderInfo) -> BlockHeader { - if info.block_hash.is_some() { - warn!("Field \"block_hash\" is ignored."); - } - - BlockHeader { - version: info.version, - prev_blockhash: info.previous_block_hash, - merkle_root: info.merkle_root, - time: info.time, - height: info.height, - ext: if info.dynafed { - BlockExtData::Dynafed { - current: create_params(info.dynafed_current.expect("missing current params")), - proposed: create_params(info.dynafed_proposed.expect("missing proposed params")), - signblock_witness: info - .dynafed_witness - .expect("missing dynafed witness") - .into_iter() - .map(|b| b.0) - .collect(), - } - } else { - BlockExtData::Proof { - challenge: info.legacy_challenge.expect("missing challenge").0.into(), - solution: info.legacy_solution.expect("missing solution").0.into(), - } - }, - } -} - -fn exec_create<'a>(matches: &clap::ArgMatches<'a>) { - let info = serde_json::from_str::(&cmd::arg_or_stdin(matches, "block-info")) - .expect("invaid json JSON input"); +fn exec_create<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let block_info = cmd::arg_or_stdin(matches, "block-info").to_string(); + let raw_stdout = matches.is_present("raw-stdout"); - if info.txids.is_some() { - warn!("Field \"txids\" is ignored."); - } - - let block = Block { - header: create_block_header(info.header), - txdata: match (info.transactions, info.raw_transactions) { - (Some(_), Some(_)) => panic!("Can't provide transactions both in JSON and raw."), - (None, None) => panic!("No transactions provided."), - (Some(infos), None) => infos.into_iter().map(create_transaction).collect(), - (None, Some(raws)) => raws - .into_iter() - .map(|r| deserialize(&r.0).expect("invalid raw transaction")) - .collect(), - }, - }; + let result = client.block_create(block_info, Some(raw_stdout)).expect("failed to create block"); - let block_bytes = serialize(&block); - if matches.is_present("raw-stdout") { - ::std::io::stdout().write_all(&block_bytes).unwrap(); + if raw_stdout { + if let Some(raw_hex) = result.as_str() { + let raw_bytes = hex::decode(raw_hex).expect("invalid hex in response"); + ::std::io::stdout().write_all(&raw_bytes).unwrap(); + } } else { - print!("{}", hex::encode(&block_bytes)); + cmd::print_output(matches, &result); } } @@ -127,28 +51,20 @@ fn cmd_decode<'a>() -> clap::App<'a, 'a> { ]) } -fn exec_decode<'a>(matches: &clap::ArgMatches<'a>) { - let hex_tx = cmd::arg_or_stdin(matches, "raw-block"); - let raw_tx = hex::decode(hex_tx.as_ref()).expect("could not decode raw block hex"); +fn exec_decode<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let hex_block = cmd::arg_or_stdin(matches, "raw-block").to_string(); + let network = cmd::network(matches); - if matches.is_present("txids") { - let block: Block = deserialize(&raw_tx).expect("invalid block format"); - let info = BlockInfo { - header: crate::GetInfo::get_info(&block.header, cmd::network(matches)), - txids: Some(block.txdata.iter().map(|t| t.txid()).collect()), - transactions: None, - raw_transactions: None, - }; - cmd::print_output(matches, &info) - } else { - let header: BlockHeader = match deserialize(&raw_tx) { - Ok(header) => header, - Err(_) => { - let block: Block = deserialize(&raw_tx).expect("invalid block format"); - block.header - } - }; - let info = crate::GetInfo::get_info(&header, cmd::network(matches)); - cmd::print_output(matches, &info) - } + let network_str = match network { + Network::ElementsRegtest => Some("elementsregtest".to_string()), + Network::Liquid => Some("liquid".to_string()), + Network::LiquidTestnet => Some("liquidtestnet".to_string()), + }; + + let txids = matches.is_present("txids"); + + let result = + client.block_decode(hex_block, network_str, Some(txids)).expect("failed to decode block"); + + cmd::print_output(matches, &result); } diff --git a/hal-simplicity-client/src/cmd/keypair.rs b/hal-simplicity-client/src/cmd/keypair.rs index df84b62..b0a9c71 100644 --- a/hal-simplicity-client/src/cmd/keypair.rs +++ b/hal-simplicity-client/src/cmd/keypair.rs @@ -1,16 +1,16 @@ use clap; -use elements::bitcoin::secp256k1::{self, rand}; use crate::cmd; +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("keypair", "manipulate private and public keys") .subcommand(cmd_generate()) } -pub fn execute<'a>(matches: &clap::ArgMatches<'a>) { +pub fn execute<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { match matches.subcommand() { - ("generate", Some(m)) => exec_generate(m), + ("generate", Some(m)) => exec_generate(m, client), (_, _) => unreachable!("clap prints help"), }; } @@ -19,23 +19,7 @@ fn cmd_generate<'a>() -> clap::App<'a, 'a> { cmd::subcommand("generate", "generate a random private/public keypair").args(&[cmd::opt_yaml()]) } -fn exec_generate<'a>(matches: &clap::ArgMatches<'a>) { - #[derive(serde::Serialize)] - struct Res { - secret: secp256k1::SecretKey, - x_only: secp256k1::XOnlyPublicKey, - parity: secp256k1::Parity, - } - - let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng()); - let (x_only, parity) = public.x_only_public_key(); - - cmd::print_output( - matches, - &Res { - secret, - x_only, - parity, - }, - ); +fn exec_generate<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let result = client.keypair_generate().expect("failed to generate keypair"); + cmd::print_output(matches, &result); } diff --git a/hal-simplicity-client/src/cmd/simplicity/info.rs b/hal-simplicity-client/src/cmd/simplicity/info.rs index fc06161..93cf61b 100644 --- a/hal-simplicity-client/src/cmd/simplicity/info.rs +++ b/hal-simplicity-client/src/cmd/simplicity/info.rs @@ -2,37 +2,7 @@ // SPDX-License-Identifier: CC0-1.0 use crate::cmd; - -use super::{Error, ErrorExt as _}; - -use hal_simplicity_daemon::simplicity::{jet, Amr, Cmr, Ihr}; -use hal_simplicity_daemon::utils::hal_simplicity::{elements_address, Program}; -use simplicity::hex::parse::FromHex as _; - -use serde::Serialize; - -#[derive(Serialize)] -struct RedeemInfo { - redeem_base64: String, - witness_hex: String, - amr: Amr, - ihr: Ihr, -} - -#[derive(Serialize)] -struct ProgramInfo { - jets: &'static str, - commit_base64: String, - commit_decode: String, - type_arrow: String, - cmr: Cmr, - liquid_address_unconf: String, - liquid_testnet_address_unconf: String, - is_redeem: bool, - #[serde(flatten)] - #[serde(skip_serializing_if = "Option::is_none")] - redeem_info: Option, -} +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("info", "Parse a base64-encoded Simplicity program and decode it") @@ -53,64 +23,21 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { ]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let program = matches.value_of("program").expect("program is mandatory"); - let witness = matches.value_of("witness"); - let state = matches.value_of("state"); - - match exec_inner(program, witness, state) { +pub fn exec<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let program = matches.value_of("program").expect("program is mandatory").to_string(); + let witness = matches.value_of("witness").map(String::from); + let state = matches.value_of("state").map(String::from); + let network = if matches.is_present("liquid") { + Some("liquid".to_string()) + } else { + Some("elementsregtest".to_string()) + }; + + match client.simplicity_info(program, witness, state, network) { Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); + } } } - -fn exec_inner( - program: &str, - witness: Option<&str>, - state: Option<&str>, -) -> Result { - // In the future we should attempt to parse as a Bitcoin program if parsing as - // Elements fails. May be tricky/annoying in Rust since Program is a - // different type from Program. - let program = - Program::::from_str(program, witness).result_context("parsing program")?; - - let redeem_info = program.redeem_node().map(|node| { - let disp = node.display(); - let x = RedeemInfo { - redeem_base64: disp.program().to_string(), - witness_hex: disp.witness().to_string(), - amr: node.amr(), - ihr: node.ihr(), - }; - x // binding needed for truly stupid borrowck reasons - }); - - let state = state - .map(<[u8; 32]>::from_hex) - .transpose() - .result_context("parsing 32-byte state commitment as hex")?; - - Ok(ProgramInfo { - jets: "core", - commit_base64: program.commit_prog().to_string(), - // FIXME this is, in general, exponential in size. Need to limit it somehow; probably need upstream support - commit_decode: program.commit_prog().display_expr().to_string(), - type_arrow: program.commit_prog().arrow().to_string(), - cmr: program.cmr(), - liquid_address_unconf: elements_address( - program.cmr(), - state, - &elements::AddressParams::LIQUID, - ) - .to_string(), - liquid_testnet_address_unconf: elements_address( - program.cmr(), - state, - &elements::AddressParams::LIQUID_TESTNET, - ) - .to_string(), - is_redeem: redeem_info.is_some(), - redeem_info, - }) -} diff --git a/hal-simplicity-client/src/cmd/simplicity/mod.rs b/hal-simplicity-client/src/cmd/simplicity/mod.rs index 2157ae0..9ae3160 100644 --- a/hal-simplicity-client/src/cmd/simplicity/mod.rs +++ b/hal-simplicity-client/src/cmd/simplicity/mod.rs @@ -6,20 +6,16 @@ mod pset; mod sighash; use crate::cmd; -use hal_simplicity_daemon::simplicity::bitcoin::{Amount, Denomination}; -use hal_simplicity_daemon::simplicity::elements::confidential; -use hal_simplicity_daemon::simplicity::elements::hex::FromHex as _; -use hal_simplicity_daemon::simplicity::jet::elements::ElementsUtxo; - +use hal_simplicity::hal_simplicity_client::HalSimplicity; use serde::Serialize; #[derive(Serialize)] -struct Error { +pub struct Error { context: &'static str, error: String, } -trait ErrorExt { +pub trait ErrorExt { fn result_context(self, context: &'static str) -> Result; } @@ -39,55 +35,11 @@ pub fn subcommand<'a>() -> clap::App<'a, 'a> { .subcommand(self::sighash::cmd()) } -pub fn execute<'a>(matches: &clap::ArgMatches<'a>) { +pub fn execute<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { match matches.subcommand() { - ("info", Some(m)) => self::info::exec(m), - ("pset", Some(m)) => self::pset::exec(m), - ("sighash", Some(m)) => self::sighash::exec(m), + ("info", Some(m)) => self::info::exec(m, client), + ("pset", Some(m)) => self::pset::exec(m, client), + ("sighash", Some(m)) => self::sighash::exec(m, client), (_, _) => unreachable!("clap prints help"), }; } - -fn parse_elements_utxo(s: &str) -> Result { - let parts: Vec<&str> = s.split(':').collect(); - if parts.len() != 3 { - return Err(Error { - context: "parsing input UTXO", - error: "expected format ::".to_string(), - }); - } - // Parse scriptPubKey - let script_pubkey: elements::Script = - parts[0].parse().result_context("parsing scriptPubKey hex")?; - - // Parse asset - try as explicit AssetId first, then as confidential commitment - let asset = if parts[1].len() == 64 { - // 32 bytes = explicit AssetId - let asset_id: elements::AssetId = parts[1].parse().result_context("parsing asset hex")?; - confidential::Asset::Explicit(asset_id) - } else { - // Parse anything except 32 bytes as a confidential commitment (which must be 33 bytes) - let commitment_bytes = - Vec::from_hex(parts[1]).result_context("parsing asset commitment hex")?; - elements::confidential::Asset::from_commitment(&commitment_bytes) - .result_context("decoding asset commitment")? - }; - - // Parse value - try as BTC decimal first, then as confidential commitment - let value = if let Ok(btc_amount) = Amount::from_str_in(parts[2], Denomination::Bitcoin) { - // Explicit value in BTC - elements::confidential::Value::Explicit(btc_amount.to_sat()) - } else { - // 33 bytes = confidential commitment - let commitment_bytes = - Vec::from_hex(parts[2]).result_context("parsing value commitment hex")?; - elements::confidential::Value::from_commitment(&commitment_bytes) - .result_context("decoding value commitment")? - }; - - Ok(ElementsUtxo { - script_pubkey, - asset, - value, - }) -} diff --git a/hal-simplicity-client/src/cmd/simplicity/pset/create.rs b/hal-simplicity-client/src/cmd/simplicity/pset/create.rs index 2e17649..6b34487 100644 --- a/hal-simplicity-client/src/cmd/simplicity/pset/create.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/create.rs @@ -1,80 +1,8 @@ // Copyright 2025 Andrew Poelstra // SPDX-License-Identifier: CC0-1.0 -use super::super::{Error, ErrorExt as _}; -use super::UpdatedPset; use crate::cmd; -use elements::confidential; -use elements::pset::PartiallySignedTransaction; -use elements::{Address, AssetId, OutPoint, Transaction, TxIn, TxOut, Txid}; -use serde::Deserialize; - -use std::collections::HashMap; - -#[derive(Deserialize)] -struct InputSpec { - txid: Txid, - vout: u32, - #[serde(default)] - sequence: Option, -} - -#[derive(Deserialize)] -struct FlattenedOutputSpec { - address: String, - asset: AssetId, - #[serde(with = "elements::bitcoin::amount::serde::as_btc")] - amount: elements::bitcoin::Amount, -} - -#[derive(Deserialize)] -#[serde(untagged)] -enum OutputSpec { - Explicit { - address: String, - asset: AssetId, - #[serde(with = "elements::bitcoin::amount::serde::as_btc")] - amount: elements::bitcoin::Amount, - }, - Map(HashMap), -} - -impl OutputSpec { - fn flatten(self) -> Box>> { - match self { - Self::Map(map) => Box::new(map.into_iter().map(|(address, amount)| { - // Use liquid bitcoin asset as default for map format - let default_asset = AssetId::from_slice(&[ - 0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3, 0x9f, 0xc0, 0x3b, 0x63, 0x7f, - 0x2a, 0x4e, 0x1e, 0x64, 0xe5, 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f, 0x6d, 0x71, - 0xaa, 0x44, 0x43, 0x65, 0x4c, 0x14, - ]) - .expect("valid asset id"); - - Ok(FlattenedOutputSpec { - address, - asset: default_asset, - amount: elements::bitcoin::Amount::from_btc(amount) - .result_context("parsing amount")?, - }) - })), - Self::Explicit { - address, - asset, - amount, - } => Box::new( - Some(Ok(FlattenedOutputSpec { - address, - asset, - amount, - })) - .into_iter(), - ), - } - } -} - pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("create", "create an empty PSET").args(&cmd::opts_networks()).args(&[ cmd::arg( @@ -89,84 +17,23 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { ]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let inputs_json = matches.value_of("inputs").expect("inputs mandatory"); - let outputs_json = matches.value_of("outputs").expect("inputs mandatory"); - - match exec_inner(inputs_json, outputs_json) { - Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), - } -} - -fn exec_inner(inputs_json: &str, outputs_json: &str) -> Result { - // Parse inputs JSON - let input_specs: Vec = - serde_json::from_str(inputs_json).result_context("parsing inputs JSON")?; - - // Parse outputs JSON - support both array and map formats - let output_specs: Vec = - serde_json::from_str(outputs_json).result_context("parsing outputs JSON")?; - - // Create transaction inputs - let mut inputs = Vec::new(); - for input_spec in &input_specs { - let outpoint = OutPoint::new(input_spec.txid, input_spec.vout); - let sequence = elements::Sequence(input_spec.sequence.unwrap_or(0xffffffff)); - - inputs.push(TxIn { - previous_output: outpoint, - script_sig: elements::Script::new(), - sequence, - asset_issuance: Default::default(), - witness: Default::default(), - is_pegin: false, - }); - } - - // Create transaction outputs - let mut outputs = Vec::new(); - for output_spec in output_specs.into_iter().flat_map(OutputSpec::flatten) { - let output_spec = output_spec?; // serde has crappy error messages so we defer parsing and then have to unwrap errors - - let script_pubkey = match output_spec.address.as_str() { - "fee" => elements::Script::new(), - x => { - let addr = x.parse::
().result_context("parsing address")?; - if addr.is_blinded() { - return Err("confidential addresses are not yet supported") - .result_context("output address"); - } - addr.script_pubkey() - } - }; - - outputs.push(TxOut { - asset: confidential::Asset::Explicit(output_spec.asset), - value: confidential::Value::Explicit(output_spec.amount.to_sat()), - nonce: elements::confidential::Nonce::Null, - script_pubkey, - witness: elements::TxOutWitness::empty(), - }); - } - - // Create the transaction - let tx = Transaction { - version: 2, - lock_time: elements::LockTime::ZERO, - input: inputs, - output: outputs, +pub fn exec<'a>( + matches: &clap::ArgMatches<'a>, + client: &hal_simplicity::hal_simplicity_client::HalSimplicity, +) { + let inputs_json = matches.value_of("inputs").expect("inputs mandatory").to_string(); + let outputs_json = matches.value_of("outputs").expect("outputs mandatory").to_string(); + let network = if matches.is_present("liquid") { + Some("liquid".to_string()) + } else { + Some("elementsregtest".to_string()) }; - // Create PSET from transaction - let pset = PartiallySignedTransaction::from_tx(tx); - - Ok(UpdatedPset { - pset: pset.to_string(), - updated_values: vec![ - // FIXME we technically update a whole slew of fields; see the implementation - // of PartiallySignedTransaction::from_tx. Should we attempt to exhaustively - // list them here? Or list none? Or what? - ], - }) + match client.pset_create(inputs_json, outputs_json, network) { + Ok(response) => cmd::print_output(matches, &response), + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); + } + } } diff --git a/hal-simplicity-client/src/cmd/simplicity/pset/extract.rs b/hal-simplicity-client/src/cmd/simplicity/pset/extract.rs index 225767a..1078a4e 100644 --- a/hal-simplicity-client/src/cmd/simplicity/pset/extract.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/extract.rs @@ -1,10 +1,8 @@ // Copyright 2025 Andrew Poelstra // SPDX-License-Identifier: CC0-1.0 -use elements::encode::serialize_hex; - -use super::super::{Error, ErrorExt as _}; use crate::cmd; +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("extract", "extract a raw transaction from a completed PSET") @@ -12,18 +10,14 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { .args(&[cmd::arg("pset", "PSET to update (base64)").takes_value(true).required(true)]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let pset_b64 = matches.value_of("pset").expect("tx mandatory"); - match exec_inner(pset_b64) { - Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), - } -} +pub fn exec<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let pset_b64 = matches.value_of("pset").expect("tx mandatory").to_string(); -fn exec_inner(pset_b64: &str) -> Result { - let pset: elements::pset::PartiallySignedTransaction = - pset_b64.parse().result_context("decoding PSET")?; - - let tx = pset.extract_tx().result_context("extracting transaction")?; - Ok(serialize_hex(&tx)) + match client.pset_extract(pset_b64) { + Ok(response) => cmd::print_output(matches, &response), + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); + } + } } diff --git a/hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs b/hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs index fee352d..12d00fb 100644 --- a/hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/finalize.rs @@ -3,12 +3,6 @@ use crate::cmd; -use hal_simplicity_daemon::simplicity::jet; -use hal_simplicity_daemon::utils::hal_simplicity::Program; - -use super::super::{Error, ErrorExt as _}; -use super::UpdatedPset; - pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("finalize", "Attach a Simplicity program and witness to a PSET input") .args(&cmd::opts_networks()) @@ -30,54 +24,22 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { ]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let pset_b64 = matches.value_of("pset").expect("tx mandatory"); - let input_idx = matches.value_of("input-index").expect("input-idx is mandatory"); - let program = matches.value_of("program").expect("program is mandatory"); - let witness = matches.value_of("witness").expect("witness is mandatory"); - let genesis_hash = matches.value_of("genesis-hash"); - - match exec_inner(pset_b64, input_idx, program, witness, genesis_hash) { - Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), +pub fn exec<'a>( + matches: &clap::ArgMatches<'a>, + client: &hal_simplicity::hal_simplicity_client::HalSimplicity, +) { + let pset_b64 = matches.value_of("pset").expect("tx mandatory").to_string(); + let input_idx_str = matches.value_of("input-index").expect("input-idx is mandatory"); + let input_idx: u32 = input_idx_str.parse().expect("invalid input index"); + let program = matches.value_of("program").expect("program is mandatory").to_string(); + let witness = matches.value_of("witness").expect("witness is mandatory").to_string(); + let genesis_hash = matches.value_of("genesis-hash").map(String::from); + + match client.pset_finalize(pset_b64, input_idx, program, witness, genesis_hash) { + Ok(response) => cmd::print_output(matches, &response), + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); + } } } - -#[allow(clippy::too_many_arguments)] -fn exec_inner( - pset_b64: &str, - input_idx: &str, - program: &str, - witness: &str, - genesis_hash: Option<&str>, -) -> Result { - // 1. Parse everything. - let mut pset: elements::pset::PartiallySignedTransaction = - pset_b64.parse().result_context("decoding PSET")?; - let input_idx: u32 = input_idx.parse().result_context("parsing input-idx")?; - let input_idx_usize = input_idx as usize; // 32->usize cast ok on almost all systems - - let program = Program::::from_str(program, Some(witness)) - .result_context("parsing program")?; - - // 2. Extract transaction environment. - let (tx_env, control_block, tap_leaf) = - super::execution_environment(&pset, input_idx_usize, program.cmr(), genesis_hash)?; - let cb_serialized = control_block.serialize(); - - // 3. Prune program. - let redeem_node = program.redeem_node().expect("populated"); - let pruned = redeem_node.prune(&tx_env).result_context("pruning program")?; - - let (prog, witness) = pruned.to_vec_with_witness(); - // If `execution_environment` above succeeded we are guaranteed that this index is in bounds. - let input = &mut pset.inputs_mut()[input_idx_usize]; - input.final_script_witness = Some(vec![witness, prog, tap_leaf.into_bytes(), cb_serialized]); - - let updated_values = vec!["final_script_witness"]; - - Ok(UpdatedPset { - pset: pset.to_string(), - updated_values, - }) -} diff --git a/hal-simplicity-client/src/cmd/simplicity/pset/mod.rs b/hal-simplicity-client/src/cmd/simplicity/pset/mod.rs index f51546f..e39b697 100644 --- a/hal-simplicity-client/src/cmd/simplicity/pset/mod.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/mod.rs @@ -7,26 +7,8 @@ mod finalize; mod run; mod update_input; -use std::sync::Arc; - -use super::{Error, ErrorExt as _}; use crate::cmd; -use elements::hashes::Hash as _; -use elements::pset::PartiallySignedTransaction; -use elements::taproot::ControlBlock; -use elements::Script; -use hal_simplicity_daemon::simplicity::elements::Transaction; -use hal_simplicity_daemon::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; -use hal_simplicity_daemon::simplicity::Cmr; -use serde::Serialize; - -#[derive(Serialize)] -struct UpdatedPset { - pset: String, - updated_values: Vec<&'static str>, -} - pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("pset", "manipulate PSETs for spending from Simplicity programs") .subcommand(self::create::cmd()) @@ -36,87 +18,16 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { .subcommand(self::update_input::cmd()) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { +pub fn exec<'a>( + matches: &clap::ArgMatches<'a>, + client: &hal_simplicity::hal_simplicity_client::HalSimplicity, +) { match matches.subcommand() { - ("create", Some(m)) => self::create::exec(m), - ("extract", Some(m)) => self::extract::exec(m), - ("finalize", Some(m)) => self::finalize::exec(m), - ("run", Some(m)) => self::run::exec(m), - ("update-input", Some(m)) => self::update_input::exec(m), + ("create", Some(m)) => self::create::exec(m, client), + ("extract", Some(m)) => self::extract::exec(m, client), + ("finalize", Some(m)) => self::finalize::exec(m, client), + ("run", Some(m)) => self::run::exec(m, client), + ("update-input", Some(m)) => self::update_input::exec(m, client), (_, _) => unreachable!("clap prints help"), }; } - -fn execution_environment( - pset: &PartiallySignedTransaction, - input_idx: usize, - cmr: Cmr, - genesis_hash: Option<&str>, -) -> Result<(ElementsEnv>, ControlBlock, Script), Error> { - let n_inputs = pset.n_inputs(); - let input = pset - .inputs() - .get(input_idx) - .ok_or_else(|| { - format!("index {} out-of-range for PSET with {} inputs", input_idx, n_inputs) - }) - .result_context("parsing input index")?; - - // Default to Liquid Testnet genesis block - let genesis_hash = match genesis_hash { - Some(s) => s.parse().result_context("parsing genesis hash")?, - None => elements::BlockHash::from_byte_array([ - // copied out of simplicity-webide source - 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, - 0x79, 0x3b, 0x5b, 0x5e, 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, - 0x8e, 0xda, 0x71, 0xa7, - ]), - }; - - // Unlike in the 'update-input' case we don't insist on any particular form of - // the Taptree. We just look for the CMR in the list. - let mut control_block_leaf = None; - for (cb, script_ver) in &input.tap_scripts { - if script_ver.1 == simplicity::leaf_version() && &script_ver.0[..] == cmr.as_ref() { - control_block_leaf = Some((cb.clone(), script_ver.0.clone())); - } - } - let (control_block, tap_leaf) = match control_block_leaf { - Some((cb, leaf)) => (cb, leaf), - None => { - return Err(format!("could not find Simplicity leaf in PSET taptree with CMR {}; did you forget to run 'simplicity pset update-input'?", cmr)) - .result_context("PSET tap_scripts field") - } - }; - - let tx = pset.extract_tx().result_context("extracting transaction from PSET")?; - let tx = Arc::new(tx); - - let input_utxos = pset - .inputs() - .iter() - .enumerate() - .map(|(n, input)| match input.witness_utxo { - Some(ref utxo) => Ok(ElementsUtxo { - script_pubkey: utxo.script_pubkey.clone(), - asset: utxo.asset, - value: utxo.value, - }), - None => Err(format!("witness_utxo field not populated for input {n}")), - }) - .collect::, _>>() - .result_context("extracting input UTXO information")?; - - let tx_env = ElementsEnv::new( - tx, - input_utxos, - input_idx as u32, // cast fine, input indices are always small - cmr, - control_block.clone(), - None, // FIXME populate this; needs https://github.com/BlockstreamResearch/rust-simplicity/issues/315 first - genesis_hash, - ); - - // 3. Prune program. - Ok((tx_env, control_block, tap_leaf)) -} diff --git a/hal-simplicity-client/src/cmd/simplicity/pset/run.rs b/hal-simplicity-client/src/cmd/simplicity/pset/run.rs index 1f94657..61b341c 100644 --- a/hal-simplicity-client/src/cmd/simplicity/pset/run.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/run.rs @@ -3,13 +3,6 @@ use crate::cmd; -use hal_simplicity_daemon::simplicity::bit_machine::{BitMachine, ExecTracker}; -use hal_simplicity_daemon::simplicity::jet; -use hal_simplicity_daemon::simplicity::{Cmr, Ihr}; -use hal_simplicity_daemon::utils::hal_simplicity::Program; - -use super::super::{Error, ErrorExt as _}; - pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("run", "Run a Simplicity program in the context of a PSET input.") .args(&cmd::opts_networks()) @@ -31,123 +24,22 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { ]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let pset_b64 = matches.value_of("pset").expect("tx mandatory"); - let input_idx = matches.value_of("input-index").expect("input-idx is mandatory"); - let program = matches.value_of("program").expect("program is mandatory"); - let witness = matches.value_of("witness").expect("witness is mandatory"); - let genesis_hash = matches.value_of("genesis-hash"); - - match exec_inner(pset_b64, input_idx, program, witness, genesis_hash) { - Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), - } -} - -#[derive(serde::Serialize)] -struct JetCall { - jet: String, - source_ty: String, - target_ty: String, - success: bool, - input_hex: String, - output_hex: String, - #[serde(skip_serializing_if = "Option::is_none")] - equality_check: Option<(String, String)>, -} - -#[derive(serde::Serialize)] -struct Response { - success: bool, - jets: Vec, -} - -#[allow(clippy::too_many_arguments)] -fn exec_inner( - pset_b64: &str, - input_idx: &str, - program: &str, - witness: &str, - genesis_hash: Option<&str>, -) -> Result { - struct JetTracker(Vec); - impl ExecTracker for JetTracker { - fn track_left(&mut self, _: Ihr) {} - fn track_right(&mut self, _: Ihr) {} - fn track_jet_call( - &mut self, - jet: &J, - input_buffer: &[simplicity::ffi::ffi::UWORD], - output_buffer: &[simplicity::ffi::ffi::UWORD], - success: bool, - ) { - // The word slices are in reverse order for some reason. - // FIXME maybe we should attempt to parse out Simplicity values here which - // can often be displayed in a better way, esp for e.g. option types. - let mut input_hex = String::new(); - for word in input_buffer.iter().rev() { - for byte in word.to_be_bytes() { - input_hex.push_str(&format!("{:02x}", byte)); - } - } - - let mut output_hex = String::new(); - for word in output_buffer.iter().rev() { - for byte in word.to_be_bytes() { - output_hex.push_str(&format!("{:02x}", byte)); - } - } - - let jet_name = jet.to_string(); - let equality_check = match jet_name.as_str() { - "eq_1" => None, // FIXME parse bits out of input - "eq_2" => None, // FIXME parse bits out of input - x if x.strip_prefix("eq_").is_some() => { - let split = input_hex.split_at(input_hex.len() / 2); - Some((split.0.to_owned(), split.1.to_owned())) - } - _ => None, - }; - self.0.push(JetCall { - jet: jet_name, - source_ty: jet.source_ty().to_final().to_string(), - target_ty: jet.target_ty().to_final().to_string(), - success, - input_hex, - output_hex, - equality_check, - }); - } - - fn track_dbg_call(&mut self, _: &Cmr, _: simplicity::Value) {} - fn is_track_debug_enabled(&self) -> bool { - false +pub fn exec<'a>( + matches: &clap::ArgMatches<'a>, + client: &hal_simplicity::hal_simplicity_client::HalSimplicity, +) { + let pset_b64 = matches.value_of("pset").expect("tx mandatory").to_string(); + let input_idx_str = matches.value_of("input-index").expect("input-idx is mandatory"); + let input_idx: u32 = input_idx_str.parse().expect("invalid input index"); + let program = matches.value_of("program").expect("program is mandatory").to_string(); + let witness = matches.value_of("witness").expect("witness is mandatory").to_string(); + let genesis_hash = matches.value_of("genesis-hash").map(String::from); + + match client.pset_run(pset_b64, input_idx, program, witness, genesis_hash) { + Ok(response) => cmd::print_output(matches, &response), + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); } } - - // 1. Parse everything. - let pset: elements::pset::PartiallySignedTransaction = - pset_b64.parse().result_context("decoding PSET")?; - let input_idx: u32 = input_idx.parse().result_context("parsing input-idx")?; - let input_idx_usize = input_idx as usize; // 32->usize cast ok on almost all systems - - let program = Program::::from_str(program, Some(witness)) - .result_context("parsing program")?; - - // 2. Extract transaction environment. - let (tx_env, _control_block, _tap_leaf) = - super::execution_environment(&pset, input_idx_usize, program.cmr(), genesis_hash)?; - - // 3. Prune program. - let redeem_node = program.redeem_node().expect("populated"); - - let mut mac = - BitMachine::for_program(redeem_node).result_context("constructing bit machine")?; - let mut tracker = JetTracker(vec![]); - // Eat success/failure. FIXME should probably report this to the user. - let success = mac.exec_with_tracker(redeem_node, &tx_env, &mut tracker).is_ok(); - Ok(Response { - success, - jets: tracker.0, - }) } diff --git a/hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs b/hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs index fa61275..36a836e 100644 --- a/hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs +++ b/hal-simplicity-client/src/cmd/simplicity/pset/update_input.rs @@ -3,16 +3,6 @@ use crate::cmd; -use core::str::FromStr; -use std::collections::BTreeMap; - -use super::super::{Error, ErrorExt as _}; -use super::UpdatedPset; - -use elements::schnorr::XOnlyPublicKey; -use hal_simplicity_daemon::utils::hal_simplicity::taproot_spend_info; -use simplicity::hex::parse::FromHex as _; - pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("update-input", "Attach UTXO data to a PSET input") .args(&cmd::opts_networks()) @@ -44,107 +34,24 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { ]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let pset_b64 = matches.value_of("pset").expect("tx mandatory"); - let input_idx = matches.value_of("input-index").expect("input-idx is mandatory"); - let input_utxo = matches.value_of("input-utxo").expect("input-utxois mandatory"); - - let internal_key = matches.value_of("internal-key"); - let cmr = matches.value_of("cmr"); - let state = matches.value_of("state"); - - match exec_inner(pset_b64, input_idx, input_utxo, internal_key, cmr, state) { - Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), - } -} - -#[allow(clippy::too_many_arguments)] -fn exec_inner( - pset_b64: &str, - input_idx: &str, - input_utxo: &str, - internal_key: Option<&str>, - cmr: Option<&str>, - state: Option<&str>, -) -> Result { - let mut pset: elements::pset::PartiallySignedTransaction = - pset_b64.parse().result_context("decoding PSET")?; - let input_idx: usize = input_idx.parse().result_context("parsing input-idx")?; - let input_utxo = super::super::parse_elements_utxo(input_utxo)?; - - let n_inputs = pset.n_inputs(); - let input = pset - .inputs_mut() - .get_mut(input_idx) - .ok_or_else(|| { - format!("index {} out-of-range for PSET with {} inputs", input_idx, n_inputs) - }) - .result_context("parsing input index")?; - - let cmr = cmr.map(simplicity::Cmr::from_str).transpose().result_context("parsing CMR")?; - let internal_key = internal_key - .map(XOnlyPublicKey::from_str) - .transpose() - .result_context("parsing internal key")?; - if cmr.is_some() && internal_key.is_none() { - return Err("internal key must be present if CMR is; PSET requires a control block for each CMR, which in turn requires the internal key. If you don't know the internal key, good chance it is the BIP-0341 'unspendable key' 50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 or the web IDE's 'unspendable key' (highly discouraged for use in production) of f5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d2") - .result_context("missing internal key"); - } - - if !input_utxo.script_pubkey.is_v1_p2tr() { - return Err("input UTXO does not appear to be a Taproot output") - .result_context("input UTXO"); - } - - // FIXME state is meaningless without CMR; should we warn here - // FIXME also should we warn if you don't provide a CMR? seems like if you're calling `simplicity pset update-input` - // you probably have a simplicity program right? maybe we should even provide a --no-cmr flag - let state = state - .map(<[u8; 32]>::from_hex) - .transpose() - .result_context("parsing 32-byte state commitment as hex")?; - - let mut updated_values = vec![]; - if let Some(internal_key) = internal_key { - updated_values.push("tap_internal_key"); - input.tap_internal_key = Some(internal_key); - // FIXME should we check whether we're using the "bad" internal key - // from the web IDE, and warn or something? - if let Some(cmr) = cmr { - // Guess that the given program is the only Tapleaf. This is the case for addresses - // generated from the web IDE, and from `hal-simplicity simplicity info`, and for - // most "test" scenarios. We need to design an API to handle more general cases. - let spend_info = taproot_spend_info(internal_key, state, cmr); - if spend_info.output_key().as_inner().serialize() != input_utxo.script_pubkey[2..] { - // If our guess was wrong, at least error out.. - return Err(format!("CMR and internal key imply output key {}, which does not match input scriptPubKey {}", spend_info.output_key().as_inner(), input_utxo.script_pubkey)) - .result_context("input UTXO"); - } - - // FIXME these unwraps and clones should be fixed by a new rust-bitcoin taproot API - let script_ver = spend_info.as_script_map().keys().next().unwrap(); - let cb = spend_info.control_block(script_ver).unwrap(); - input.tap_merkle_root = spend_info.merkle_root(); - input.tap_scripts = BTreeMap::new(); - input.tap_scripts.insert(cb, script_ver.clone()); - updated_values.push("tap_merkle_root"); - updated_values.push("tap_scripts"); +pub fn exec<'a>( + matches: &clap::ArgMatches<'a>, + client: &hal_simplicity::hal_simplicity_client::HalSimplicity, +) { + let pset_b64 = matches.value_of("pset").expect("tx mandatory").to_string(); + let input_idx_str = matches.value_of("input-index").expect("input-idx is mandatory"); + let input_idx: u32 = input_idx_str.parse().expect("invalid input index"); + let input_utxo = matches.value_of("input-utxo").expect("input-utxo is mandatory").to_string(); + + let internal_key = matches.value_of("internal-key").map(String::from); + let cmr = matches.value_of("cmr").map(String::from); + let state = matches.value_of("state").map(String::from); + + match client.pset_update_input(pset_b64, input_idx, input_utxo, internal_key, cmr, state) { + Ok(response) => cmd::print_output(matches, &response), + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); } } - - // FIXME should we bother erroring or warning if we clobber this or other fields? - input.witness_utxo = Some(elements::TxOut { - asset: input_utxo.asset, - value: input_utxo.value, - nonce: elements::confidential::Nonce::Null, // not in UTXO set, irrelevant to PSET - script_pubkey: input_utxo.script_pubkey, - witness: elements::TxOutWitness::empty(), // not in UTXO set, irrelevant to PSET - }); - updated_values.push("witness_utxo"); - - Ok(UpdatedPset { - pset: pset.to_string(), - updated_values, - }) } diff --git a/hal-simplicity-client/src/cmd/simplicity/sighash.rs b/hal-simplicity-client/src/cmd/simplicity/sighash.rs index c98b680..d3cec7a 100644 --- a/hal-simplicity-client/src/cmd/simplicity/sighash.rs +++ b/hal-simplicity-client/src/cmd/simplicity/sighash.rs @@ -2,30 +2,7 @@ // SPDX-License-Identifier: CC0-1.0 use crate::cmd; - -use super::{Error, ErrorExt as _}; - -use elements::hashes::Hash as _; -use elements::pset::PartiallySignedTransaction; -use hal_simplicity_daemon::simplicity::bitcoin::secp256k1::{ - schnorr, Keypair, Message, Secp256k1, SecretKey, -}; -use hal_simplicity_daemon::simplicity::elements; -use hal_simplicity_daemon::simplicity::elements::hashes::sha256; -use hal_simplicity_daemon::simplicity::elements::hex::FromHex; -use hal_simplicity_daemon::simplicity::elements::taproot::ControlBlock; - -use hal_simplicity_daemon::simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; -use hal_simplicity_daemon::simplicity::Cmr; - -use serde::Serialize; - -#[derive(Serialize)] -struct SighashInfo { - sighash: sha256::Hash, - signature: Option, - valid_signature: Option, -} +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn cmd<'a>() -> clap::App<'a, 'a> { cmd::subcommand("sighash", "Compute signature hashes or signatures for use with Simplicity") @@ -63,18 +40,20 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> { ]) } -pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { - let tx_hex = matches.value_of("tx").expect("tx mandatory"); - let input_idx = matches.value_of("input-index").expect("input-idx is mandatory"); - let cmr = matches.value_of("cmr").expect("cmr is mandatory"); - let control_block = matches.value_of("control-block"); - let genesis_hash = matches.value_of("genesis-hash"); - let secret_key = matches.value_of("secret-key"); - let public_key = matches.value_of("public-key"); - let signature = matches.value_of("signature"); - let input_utxos: Option> = matches.values_of("input-utxo").map(|vals| vals.collect()); - - match exec_inner( +pub fn exec<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let tx_hex = matches.value_of("tx").expect("tx mandatory").to_string(); + let input_idx_str = matches.value_of("input-index").expect("input-idx is mandatory"); + let input_idx: u32 = input_idx_str.parse().expect("invalid input index"); + let cmr = matches.value_of("cmr").expect("cmr is mandatory").to_string(); + let control_block = matches.value_of("control-block").map(String::from); + let genesis_hash = matches.value_of("genesis-hash").map(String::from); + let secret_key = matches.value_of("secret-key").map(String::from); + let public_key = matches.value_of("public-key").map(String::from); + let signature = matches.value_of("signature").map(String::from); + let input_utxos: Option> = + matches.values_of("input-utxo").map(|vals| vals.map(String::from).collect()); + + match client.simplicity_sighash( tx_hex, input_idx, cmr, @@ -83,169 +62,12 @@ pub fn exec<'a>(matches: &clap::ArgMatches<'a>) { secret_key, public_key, signature, - input_utxos.as_deref(), + input_utxos, ) { Ok(info) => cmd::print_output(matches, &info), - Err(e) => cmd::print_output(matches, &e), - } -} - -#[allow(clippy::too_many_arguments)] -fn exec_inner( - tx_hex: &str, - input_idx: &str, - cmr: &str, - control_block: Option<&str>, - genesis_hash: Option<&str>, - secret_key: Option<&str>, - public_key: Option<&str>, - signature: Option<&str>, - input_utxos: Option<&[&str]>, -) -> Result { - let secp = Secp256k1::new(); - - // Attempt to decode transaction as PSET first. If it succeeds, we can extract - // a lot of information from it. If not, we assume the transaction is hex and - // will give the user an error corresponding to this. - let pset = tx_hex.parse::().ok(); - - // In the future we should attempt to parse as a Bitcoin program if parsing as - // Elements fails. May be tricky/annoying in Rust since Program is a - // different type from Program. - let tx = match pset { - Some(ref pset) => pset.extract_tx().result_context("extracting transaction from PSET")?, - None => { - let tx_bytes = Vec::from_hex(tx_hex).result_context("parsing transaction hex")?; - elements::encode::deserialize(&tx_bytes).result_context("decoding transaction")? - } - }; - let input_idx: u32 = input_idx.parse().result_context("parsing input-idx")?; - let cmr: Cmr = cmr.parse().result_context("parsing cmr")?; - - // If the user specifies a control block, use it. Otherwise query the PSET. - let control_block = if let Some(cb) = control_block { - let cb_bytes = Vec::from_hex(cb).result_context("parsing control block hex")?; - // For txes from webide, the internal key in this control block will be the hardcoded - // value f5919fa64ce45f8306849072b26c1bfdd2937e6b81774796ff372bd1eb5362d2 - ControlBlock::from_slice(&cb_bytes).result_context("decoding control block")? - } else if let Some(ref pset) = pset { - let n_inputs = pset.n_inputs(); - let input = pset - .inputs() - .get(input_idx as usize) // cast u32->usize probably fine - .ok_or_else(|| { - format!("index {} out-of-range for PSET with {} inputs", input_idx, n_inputs) - }) - .result_context("parsing input index")?; - - let mut control_block = None; - for (cb, script_ver) in &input.tap_scripts { - if script_ver.1 == simplicity::leaf_version() && &script_ver.0[..] == cmr.as_ref() { - control_block = Some(cb.clone()); - } + Err(e) => { + eprintln!("Error: {}", e); + std::process::exit(1); } - match control_block { - Some(cb) => cb, - None => { - return Err(format!("could not find control block in PSET for CMR {}", cmr)) - .result_context("finding control block")? - } - } - } else { - return Err("with a raw transaction, control-block must be provided") - .result_context("computing control block"); - }; - - let input_utxos = if let Some(input_utxos) = input_utxos { - input_utxos - .iter() - .map(|utxo_str| super::parse_elements_utxo(utxo_str)) - .collect::, Error>>()? - } else if let Some(ref pset) = pset { - pset.inputs() - .iter() - .enumerate() - .map(|(n, input)| match input.witness_utxo { - Some(ref utxo) => Ok(ElementsUtxo { - script_pubkey: utxo.script_pubkey.clone(), - asset: utxo.asset, - value: utxo.value, - }), - None => Err(format!("witness_utxo field not populated for input {n}")), - }) - .collect::, _>>() - .result_context("extracting input UTXO information")? - } else { - return Err("with a raw transaction, input-utxos must be provided") - .result_context("computing control block"); - }; - assert_eq!(input_utxos.len(), tx.input.len()); - - // Default to Bitcoin blockhash. - let genesis_hash = match genesis_hash { - Some(s) => s.parse().result_context("parsing genesis hash")?, - None => elements::BlockHash::from_byte_array([ - // copied out of simplicity-webide source - 0xc1, 0xb1, 0x6a, 0xe2, 0x4f, 0x24, 0x23, 0xae, 0xa2, 0xea, 0x34, 0x55, 0x22, 0x92, - 0x79, 0x3b, 0x5b, 0x5e, 0x82, 0x99, 0x9a, 0x1e, 0xed, 0x81, 0xd5, 0x6a, 0xee, 0x52, - 0x8e, 0xda, 0x71, 0xa7, - ]), - }; - - let tx_env = ElementsEnv::new( - &tx, - input_utxos, - input_idx, - cmr, - control_block, - None, // FIXME populate this; needs https://github.com/BlockstreamResearch/rust-simplicity/issues/315 first - genesis_hash, - ); - - let (pk, sig) = match (public_key, signature) { - (Some(pk), None) => (Some(pk.parse().result_context("parsing public key")?), None), - (Some(pk), Some(sig)) => ( - Some(pk.parse().result_context("parsing public key")?), - Some(sig.parse().result_context("parsing signature")?), - ), - (None, Some(_)) => { - return Err(Error { - context: "reading cli arguments", - error: "if signature is provided, public-key must be provided as well".to_owned(), - }) - } - (None, None) => (None, None), - }; - - let sighash = tx_env.c_tx_env().sighash_all(); - let sighash_msg = Message::from_digest(sighash.to_byte_array()); // FIXME can remove in next version ofrust-secp - Ok(SighashInfo { - sighash, - signature: match secret_key { - Some(sk) => { - let sk: SecretKey = sk.parse().result_context("parsing secret key hex")?; - let keypair = Keypair::from_secret_key(&secp, &sk); - - if let Some(ref pk) = pk { - if pk != &keypair.x_only_public_key().0 { - return Err(Error { - context: "checking secret key and public key consistency", - error: format!( - "secret key had public key {}, but was passed explicit public key {}", - keypair.x_only_public_key().0, - pk, - ), - }); - } - } - - Some(secp.sign_schnorr(&sighash_msg, &keypair)) - } - None => None, - }, - valid_signature: match (pk, sig) { - (Some(pk), Some(sig)) => Some(secp.verify_schnorr(&sig, &sighash_msg, &pk).is_ok()), - _ => None, - }, - }) + } } diff --git a/hal-simplicity-client/src/cmd/tx.rs b/hal-simplicity-client/src/cmd/tx.rs index 1f679b2..8461b34 100644 --- a/hal-simplicity-client/src/cmd/tx.rs +++ b/hal-simplicity-client/src/cmd/tx.rs @@ -1,28 +1,8 @@ -use std::convert::TryInto; use std::io::Write; -use clap; -use elements::bitcoin; -use elements::encode::{deserialize, serialize}; -use elements::hashes::Hash; -use elements::secp256k1_zkp::{ - Generator, PedersenCommitment, PublicKey, RangeProof, SurjectionProof, Tweak, -}; -use elements::{ - confidential, AssetIssuance, OutPoint, Script, Transaction, TxIn, TxInWitness, TxOut, - TxOutWitness, -}; -use log::warn; - use crate::cmd; -use hal_simplicity_daemon::utils::confidential::{ - ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialType, ConfidentialValueInfo, -}; -use hal_simplicity_daemon::utils::tx::{ - AssetIssuanceInfo, InputInfo, InputScriptInfo, InputWitnessInfo, OutputInfo, OutputScriptInfo, - OutputWitnessInfo, PeginDataInfo, PegoutDataInfo, TransactionInfo, -}; -use hal_simplicity_daemon::utils::Network; +use crate::Network; +use hal_simplicity::hal_simplicity_client::HalSimplicity; pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("tx", "manipulate transactions") @@ -30,10 +10,10 @@ pub fn subcommand<'a>() -> clap::App<'a, 'a> { .subcommand(cmd_decode()) } -pub fn execute<'a>(matches: &clap::ArgMatches<'a>) { +pub fn execute<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { match matches.subcommand() { - ("create", Some(m)) => exec_create(m), - ("decode", Some(m)) => exec_decode(m), + ("create", Some(m)) => exec_create(m, client), + ("decode", Some(m)) => exec_decode(m, client), (_, _) => unreachable!("clap prints help"), }; } @@ -47,405 +27,19 @@ fn cmd_create<'a>() -> clap::App<'a, 'a> { ]) } -/// Check both ways to specify the outpoint and panic if conflicting. -fn outpoint_from_input_info(input: &InputInfo) -> OutPoint { - let op1: Option = - input.prevout.as_ref().map(|op| op.parse().expect("invalid prevout format")); - let op2 = match input.txid { - Some(txid) => match input.vout { - Some(vout) => Some(OutPoint { - txid, - vout, - }), - None => panic!("\"txid\" field given in input without \"vout\" field"), - }, - None => None, - }; - - match (op1, op2) { - (Some(op1), Some(op2)) => { - if op1 != op2 { - panic!("Conflicting prevout information in input."); - } - op1 - } - (Some(op), None) => op, - (None, Some(op)) => op, - (None, None) => panic!("No previous output provided in input."), - } -} - -fn bytes_32(bytes: &[u8]) -> Option<[u8; 32]> { - if bytes.len() != 32 { - None - } else { - let mut array = [0; 32]; - for (x, y) in bytes.iter().zip(array.iter_mut()) { - *y = *x; - } - Some(array) - } -} - -fn create_confidential_value(info: ConfidentialValueInfo) -> confidential::Value { - match info.type_ { - ConfidentialType::Null => confidential::Value::Null, - ConfidentialType::Explicit => confidential::Value::Explicit( - info.value.expect("Field \"value\" is required for explicit values."), - ), - ConfidentialType::Confidential => { - let comm = PedersenCommitment::from_slice( - &info - .commitment - .expect("Field \"commitment\" is required for confidential values.") - .0[..], - ) - .expect("invalid confidential commitment"); - confidential::Value::Confidential(comm) - } - } -} - -fn create_confidential_asset(info: ConfidentialAssetInfo) -> confidential::Asset { - match info.type_ { - ConfidentialType::Null => confidential::Asset::Null, - ConfidentialType::Explicit => confidential::Asset::Explicit( - info.asset.expect("Field \"asset\" is required for explicit assets."), - ), - ConfidentialType::Confidential => { - let gen = Generator::from_slice( - &info - .commitment - .expect("Field \"commitment\" is required for confidential values.") - .0[..], - ) - .expect("invalid confidential commitment"); - confidential::Asset::Confidential(gen) - } - } -} - -fn create_confidential_nonce(info: ConfidentialNonceInfo) -> confidential::Nonce { - match info.type_ { - ConfidentialType::Null => confidential::Nonce::Null, - ConfidentialType::Explicit => confidential::Nonce::Explicit( - bytes_32(&info.nonce.expect("Field \"nonce\" is required for asset issuances.").0[..]) - .expect("wrong size of \"nonce\" field"), - ), - ConfidentialType::Confidential => { - let pubkey = PublicKey::from_slice( - &info - .commitment - .expect("Field \"commitment\" is required for confidential values.") - .0[..], - ) - .expect("invalid confidential commitment"); - confidential::Nonce::Confidential(pubkey) - } - } -} - -fn create_asset_issuance(info: AssetIssuanceInfo) -> AssetIssuance { - AssetIssuance { - asset_blinding_nonce: Tweak::from_slice( - &info - .asset_blinding_nonce - .expect("Field \"asset_blinding_nonce\" is required for asset issuances.") - .0[..], - ) - .expect("Invalid \"asset_blinding_nonce\"."), - asset_entropy: bytes_32( - &info - .asset_entropy - .expect("Field \"asset_entropy\" is required for asset issuances.") - .0[..], - ) - .expect("Invalid size of \"asset_entropy\"."), - amount: create_confidential_value( - info.amount.expect("Field \"amount\" is required for asset issuances."), - ), - inflation_keys: create_confidential_value( - info.inflation_keys.expect("Field \"inflation_keys\" is required for asset issuances."), - ), - } -} - -fn create_script_sig(ss: InputScriptInfo) -> Script { - if let Some(hex) = ss.hex { - if ss.asm.is_some() { - warn!("Field \"asm\" of input is ignored."); - } - - hex.0.into() - } else if ss.asm.is_some() { - panic!("Decoding script assembly is not yet supported."); - } else { - panic!("No scriptSig info provided."); - } -} - -fn create_pegin_witness(pd: PeginDataInfo, prevout: bitcoin::OutPoint) -> Vec> { - if prevout != pd.outpoint.parse().expect("Invalid outpoint in field \"pegin_data\".") { - panic!("Outpoint in \"pegin_data\" does not correspond to input value."); - } - - let asset = match create_confidential_asset(pd.asset) { - confidential::Asset::Explicit(asset) => asset, - _ => panic!("Asset in \"pegin_data\" should be explicit."), - }; - vec![ - serialize(&pd.value), - serialize(&asset), - pd.genesis_hash.to_byte_array().to_vec(), - serialize(&pd.claim_script.0), - serialize(&pd.mainchain_tx_hex.0), - serialize(&pd.merkle_proof.0), - ] -} - -fn convert_outpoint_to_btc(p: elements::OutPoint) -> bitcoin::OutPoint { - bitcoin::OutPoint { - txid: bitcoin::Txid::from_byte_array(p.txid.to_byte_array()), - vout: p.vout, - } -} - -fn create_input_witness( - info: Option, - pd: Option, - prevout: OutPoint, -) -> TxInWitness { - let pegin_witness = - if let Some(info_wit) = info.as_ref().and_then(|info| info.pegin_witness.as_ref()) { - if pd.is_some() { - warn!("Field \"pegin_data\" of input is ignored."); - } - info_wit.iter().map(|h| h.clone().0).collect() - } else if let Some(pd) = pd { - create_pegin_witness(pd, convert_outpoint_to_btc(prevout)) - } else { - Default::default() - }; - - if let Some(wi) = info { - TxInWitness { - amount_rangeproof: wi - .amount_rangeproof - .map(|b| Box::new(RangeProof::from_slice(&b.0).expect("invalid rangeproof"))), - inflation_keys_rangeproof: wi - .inflation_keys_rangeproof - .map(|b| Box::new(RangeProof::from_slice(&b.0).expect("invalid rangeproof"))), - script_witness: match wi.script_witness { - Some(ref w) => w.iter().map(|h| h.clone().0).collect(), - None => Vec::new(), - }, - pegin_witness, - } - } else { - TxInWitness { - pegin_witness, - ..Default::default() - } - } -} - -fn create_input(input: InputInfo) -> TxIn { - let has_issuance = input.has_issuance.unwrap_or(input.asset_issuance.is_some()); - let is_pegin = input.is_pegin.unwrap_or(input.pegin_data.is_some()); - let prevout = outpoint_from_input_info(&input); +fn exec_create<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let tx_info = cmd::arg_or_stdin(matches, "tx-info").to_string(); + let raw_stdout = matches.is_present("raw-stdout"); - TxIn { - previous_output: prevout, - script_sig: input.script_sig.map(create_script_sig).unwrap_or_default(), - sequence: elements::Sequence::from_height( - input.sequence.unwrap_or_default().try_into().unwrap(), - ), - is_pegin, - asset_issuance: if has_issuance { - input.asset_issuance.map(create_asset_issuance).unwrap_or_default() - } else { - if input.asset_issuance.is_some() { - warn!("Field \"asset_issuance\" of input is ignored."); - } - Default::default() - }, - witness: create_input_witness(input.witness, input.pegin_data, prevout), - } -} - -fn create_script_pubkey(spk: OutputScriptInfo, used_network: &mut Option) -> Script { - if spk.type_.is_some() { - warn!("Field \"type\" of output is ignored."); - } - - if let Some(hex) = spk.hex { - if spk.asm.is_some() { - warn!("Field \"asm\" of output is ignored."); - } - if spk.address.is_some() { - warn!("Field \"address\" of output is ignored."); - } - - //TODO(stevenroose) do script sanity check to avoid blackhole? - hex.0.into() - } else if spk.asm.is_some() { - if spk.address.is_some() { - warn!("Field \"address\" of output is ignored."); - } - - panic!("Decoding script assembly is not yet supported."); - } else if let Some(address) = spk.address { - // Error if another network had already been used. - if let Some(network) = Network::from_params(address.params) { - if used_network.replace(network).unwrap_or(network) != network { - panic!("Addresses for different networks are used in the output scripts."); - } - } - - address.script_pubkey() - } else { - panic!("No scriptPubKey info provided."); - } -} - -fn create_bitcoin_script_pubkey(spk: hal::tx::OutputScriptInfo) -> bitcoin::ScriptBuf { - if spk.type_.is_some() { - warn!("Field \"type\" of output is ignored."); - } - - if let Some(hex) = spk.hex { - if spk.asm.is_some() { - warn!("Field \"asm\" of output is ignored."); - } - if spk.address.is_some() { - warn!("Field \"address\" of output is ignored."); - } + let result = client.tx_create(tx_info, Some(raw_stdout)).expect("failed to create transaction"); - //TODO(stevenroose) do script sanity check to avoid blackhole? - hex.0.into() - } else if spk.asm.is_some() { - if spk.address.is_some() { - warn!("Field \"address\" of output is ignored."); + if raw_stdout { + if let Some(raw_hex) = result.as_str() { + let raw_bytes = hex::decode(raw_hex).expect("invalid hex in response"); + ::std::io::stdout().write_all(&raw_bytes).unwrap(); } - - panic!("Decoding script assembly is not yet supported."); - } else if let Some(address) = spk.address { - address.assume_checked().script_pubkey() - } else { - panic!("No scriptPubKey info provided."); - } -} - -fn create_output_witness(w: OutputWitnessInfo) -> TxOutWitness { - TxOutWitness { - surjection_proof: w.surjection_proof.map(|b| { - Box::new(SurjectionProof::from_slice(&b.0[..]).expect("invalid surjection proof")) - }), - rangeproof: w - .rangeproof - .map(|b| Box::new(RangeProof::from_slice(&b.0[..]).expect("invalid rangeproof"))), - } -} - -fn create_script_pubkey_from_pegout_data(pd: PegoutDataInfo) -> Script { - let mut builder = elements::script::Builder::new() - .push_opcode(elements::opcodes::all::OP_RETURN) - .push_slice(&pd.genesis_hash.to_byte_array()) - .push_slice(create_bitcoin_script_pubkey(pd.script_pub_key).as_bytes()); - for d in pd.extra_data { - builder = builder.push_slice(&d.0); - } - builder.into_script() -} - -fn create_output(output: OutputInfo) -> TxOut { - // Keep track of which network has been used in addresses and error if two different networks - // are used. - let mut used_network = None; - let value = output - .value - .map(create_confidential_value) - .expect("Field \"value\" is required for outputs."); - let asset = output - .asset - .map(create_confidential_asset) - .expect("Field \"asset\" is required for outputs."); - - TxOut { - asset, - value, - nonce: output.nonce.map(create_confidential_nonce).unwrap_or(confidential::Nonce::Null), - script_pubkey: if let Some(spk) = output.script_pub_key { - if output.pegout_data.is_some() { - warn!("Field \"pegout_data\" of output is ignored."); - } - create_script_pubkey(spk, &mut used_network) - } else if let Some(pd) = output.pegout_data { - match value { - confidential::Value::Explicit(v) => { - if v != pd.value { - panic!("Value in \"pegout_data\" does not correspond to output value."); - } - } - _ => panic!("Explicit value is required for pegout data."), - } - if asset != create_confidential_asset(pd.asset.clone()) { - panic!("Asset in \"pegout_data\" does not correspond to output value."); - } - create_script_pubkey_from_pegout_data(pd) - } else { - Default::default() - }, - witness: output.witness.map(create_output_witness).unwrap_or_default(), - } -} - -pub fn create_transaction(info: TransactionInfo) -> Transaction { - // Fields that are ignored. - if info.txid.is_some() { - warn!("Field \"txid\" is ignored."); - } - if info.hash.is_some() { - warn!("Field \"hash\" is ignored."); - } - if info.size.is_some() { - warn!("Field \"size\" is ignored."); - } - if info.weight.is_some() { - warn!("Field \"weight\" is ignored."); - } - if info.vsize.is_some() { - warn!("Field \"vsize\" is ignored."); - } - - Transaction { - version: info.version.expect("Field \"version\" is required."), - lock_time: info.locktime.expect("Field \"locktime\" is required."), - input: info - .inputs - .expect("Field \"inputs\" is required.") - .into_iter() - .map(create_input) - .collect(), - output: info - .outputs - .expect("Field \"outputs\" is required.") - .into_iter() - .map(create_output) - .collect(), - } -} - -fn exec_create<'a>(matches: &clap::ArgMatches<'a>) { - let info = serde_json::from_str::(&cmd::arg_or_stdin(matches, "tx-info")) - .expect("invalid JSON provided"); - let tx = create_transaction(info); - - let tx_bytes = serialize(&tx); - if matches.is_present("raw-stdout") { - ::std::io::stdout().write_all(&tx_bytes).unwrap(); } else { - print!("{}", hex::encode(&tx_bytes)); + cmd::print_output(matches, &result); } } @@ -455,11 +49,17 @@ fn cmd_decode<'a>() -> clap::App<'a, 'a> { .args(&[cmd::opt_yaml(), cmd::arg("raw-tx", "the raw transaction in hex").required(false)]) } -fn exec_decode<'a>(matches: &clap::ArgMatches<'a>) { - let hex_tx = cmd::arg_or_stdin(matches, "raw-tx"); - let raw_tx = hex::decode(hex_tx.as_ref()).expect("could not decode raw tx"); - let tx: Transaction = deserialize(&raw_tx).expect("invalid tx format"); +fn exec_decode<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { + let hex_tx = cmd::arg_or_stdin(matches, "raw-tx").to_string(); + let network = cmd::network(matches); + + let network_str = match network { + Network::ElementsRegtest => Some("elementsregtest".to_string()), + Network::Liquid => Some("liquid".to_string()), + Network::LiquidTestnet => Some("liquidtestnet".to_string()), + }; + + let result = client.tx_decode(hex_tx, network_str).expect("failed to decode transaction"); - let info = crate::GetInfo::get_info(&tx, cmd::network(matches)); - cmd::print_output(matches, &info) + cmd::print_output(matches, &result); } diff --git a/hal-simplicity-client/src/lib.rs b/hal-simplicity-client/src/lib.rs index 4a233ca..36e6bf2 100644 --- a/hal-simplicity-client/src/lib.rs +++ b/hal-simplicity-client/src/lib.rs @@ -1,3 +1,5 @@ pub extern crate simplicity; pub mod hal_simplicity_client; + +pub use hal_simplicity_client::HalSimplicity; diff --git a/hal-simplicity-client/src/main.rs b/hal-simplicity-client/src/main.rs index 7c8270e..8d51f1f 100644 --- a/hal-simplicity-client/src/main.rs +++ b/hal-simplicity-client/src/main.rs @@ -7,6 +7,8 @@ pub use hal_simplicity_daemon::utils::{GetInfo, Network}; pub mod cmd; +use hal_simplicity::hal_simplicity_client::HalSimplicity; + /// Setup logging with the given log level. fn setup_logger(lvl: log::LevelFilter) { fern::Dispatch::new() @@ -34,16 +36,17 @@ fn init_app<'a, 'b>() -> clap::App<'a, 'b> { .takes_value(false) .global(true), ) + .arg(cmd::opt("daemon-url", "URL of hal-simplicity-daemon").takes_value(true).global(true)) } /// Try execute built-in command. Return false if no command found. -fn execute_builtin<'a>(matches: &clap::ArgMatches<'a>) -> bool { +fn execute_builtin<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) -> bool { match matches.subcommand() { - ("address", Some(m)) => cmd::address::execute(m), - ("block", Some(m)) => cmd::block::execute(m), - ("keypair", Some(m)) => cmd::keypair::execute(m), - ("simplicity", Some(m)) => cmd::simplicity::execute(m), - ("tx", Some(m)) => cmd::tx::execute(m), + ("address", Some(m)) => cmd::address::execute(m, client), + ("block", Some(m)) => cmd::block::execute(m, client), + ("keypair", Some(m)) => cmd::keypair::execute(m, client), + ("simplicity", Some(m)) => cmd::simplicity::execute(m, client), + ("tx", Some(m)) => cmd::tx::execute(m, client), _ => return false, }; true @@ -73,7 +76,19 @@ fn main() { false => setup_logger(log::LevelFilter::Warn), } - if execute_builtin(&matches) { + // Create JSON-RPC client + let client = if let Some(url) = matches.value_of("daemon-url") { + HalSimplicity::new(url.to_string()) + } else { + HalSimplicity::default() + }; + + let client = client.unwrap_or_else(|e| { + eprintln!("Failed to connect to daemon: {}", e); + process::exit(1); + }); + + if execute_builtin(&matches, &client) { // success process::exit(0); } else { From 5c9436b4def4b412d42f5e4b82ac3ff23c98927a Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 11:59:51 +0200 Subject: [PATCH 20/39] Add the custom serde for parity --- hal-simplicity-daemon/src/types.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/hal-simplicity-daemon/src/types.rs b/hal-simplicity-daemon/src/types.rs index 33af263..4a2992a 100644 --- a/hal-simplicity-daemon/src/types.rs +++ b/hal-simplicity-daemon/src/types.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; // Re-exports for proper serialization pub use elements::bitcoin::secp256k1; @@ -6,6 +6,27 @@ pub use elements::hashes::sha256; pub use simplicity::bitcoin::secp256k1::schnorr; pub use simplicity::{Amr, Cmr, Ihr}; +// Custom serialization for Parity as 0 or 1 +mod parity_serde { + use super::*; + + pub fn serialize(parity: &secp256k1::Parity, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u8(parity.to_i32() as u8) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = u8::deserialize(deserializer)?; + secp256k1::Parity::from_i32(value as i32) + .map_err(|_| serde::de::Error::custom(format!("invalid parity value: {}", value))) + } +} + // Address types #[derive(Debug, Serialize, Deserialize)] pub struct AddressCreateRequest { @@ -55,6 +76,7 @@ pub struct KeypairGenerateRequest {} pub struct KeypairGenerateResponse { pub secret: secp256k1::SecretKey, pub x_only: secp256k1::XOnlyPublicKey, + #[serde(with = "parity_serde")] pub parity: secp256k1::Parity, } From ab53d3b14f81f0618cfbe601f22090f1f19d194e Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:14:42 +0200 Subject: [PATCH 21/39] Configure the embedded CLI daemon to only run if required --- hal-simplicity-client/src/cmd/address.rs | 3 +- hal-simplicity-client/src/cmd/block.rs | 3 +- hal-simplicity-client/src/cmd/mod.rs | 2 +- hal-simplicity-client/src/cmd/tx.rs | 3 +- hal-simplicity-client/src/main.rs | 13 +++++-- .../src/daemon/actions/address.rs | 6 ++-- .../src/daemon/actions/block.rs | 3 +- .../src/daemon/actions/tx.rs | 3 +- hal-simplicity-daemon/src/lib.rs | 2 ++ hal-simplicity-daemon/src/types.rs | 32 +++++++++++++++++ hal-simplicity-daemon/src/utils/mod.rs | 34 +------------------ hal-simplicity-daemon/src/utils/tx.rs | 3 +- 12 files changed, 60 insertions(+), 47 deletions(-) diff --git a/hal-simplicity-client/src/cmd/address.rs b/hal-simplicity-client/src/cmd/address.rs index 6ca2c2c..352ddaa 100644 --- a/hal-simplicity-client/src/cmd/address.rs +++ b/hal-simplicity-client/src/cmd/address.rs @@ -1,9 +1,10 @@ use clap; use crate::cmd; -use crate::Network; use hal_simplicity::hal_simplicity_client::HalSimplicity; +use hal_simplicity_daemon::Network; + pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("address", "work with addresses") .subcommand(cmd_create()) diff --git a/hal-simplicity-client/src/cmd/block.rs b/hal-simplicity-client/src/cmd/block.rs index b87ad12..8accba7 100644 --- a/hal-simplicity-client/src/cmd/block.rs +++ b/hal-simplicity-client/src/cmd/block.rs @@ -1,9 +1,10 @@ use std::io::Write; use crate::cmd; -use crate::Network; use hal_simplicity::hal_simplicity_client::HalSimplicity; +use hal_simplicity_daemon::Network; + pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("block", "manipulate blocks") .subcommand(cmd_create()) diff --git a/hal-simplicity-client/src/cmd/mod.rs b/hal-simplicity-client/src/cmd/mod.rs index ab1b019..08291e0 100644 --- a/hal-simplicity-client/src/cmd/mod.rs +++ b/hal-simplicity-client/src/cmd/mod.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::io; use std::io::Read; -use hal_simplicity_daemon::utils::Network; +use hal_simplicity_daemon::Network; /// Build a list of all built-in subcommands. pub fn subcommands<'a>() -> Vec> { diff --git a/hal-simplicity-client/src/cmd/tx.rs b/hal-simplicity-client/src/cmd/tx.rs index 8461b34..58b8697 100644 --- a/hal-simplicity-client/src/cmd/tx.rs +++ b/hal-simplicity-client/src/cmd/tx.rs @@ -1,9 +1,10 @@ use std::io::Write; use crate::cmd; -use crate::Network; use hal_simplicity::hal_simplicity_client::HalSimplicity; +use hal_simplicity_daemon::Network; + pub fn subcommand<'a>() -> clap::App<'a, 'a> { cmd::subcommand_group("tx", "manipulate transactions") .subcommand(cmd_create()) diff --git a/hal-simplicity-client/src/main.rs b/hal-simplicity-client/src/main.rs index 8d51f1f..6a9262d 100644 --- a/hal-simplicity-client/src/main.rs +++ b/hal-simplicity-client/src/main.rs @@ -3,8 +3,6 @@ use std::process; pub use elements::bitcoin; -pub use hal_simplicity_daemon::utils::{GetInfo, Network}; - pub mod cmd; use hal_simplicity::hal_simplicity_client::HalSimplicity; @@ -80,7 +78,16 @@ fn main() { let client = if let Some(url) = matches.value_of("daemon-url") { HalSimplicity::new(url.to_string()) } else { - HalSimplicity::default() + #[cfg(feature = "embed_daemon")] + { + HalSimplicity::default() + } + #[cfg(not(feature = "embed_daemon"))] + { + eprintln!("Error: --daemon-url is required when embed_daemon feature is disabled"); + eprintln!("Usage: hal-simplicity --daemon-url "); + process::exit(1); + } }; let client = client.unwrap_or_else(|e| { diff --git a/hal-simplicity-daemon/src/daemon/actions/address.rs b/hal-simplicity-daemon/src/daemon/actions/address.rs index 12414d4..43355f1 100644 --- a/hal-simplicity-daemon/src/daemon/actions/address.rs +++ b/hal-simplicity-daemon/src/daemon/actions/address.rs @@ -3,12 +3,10 @@ use elements::hashes::Hash; use elements::{Address, WPubkeyHash, WScriptHash}; use thiserror::Error; -use crate::utils::{ - address::{AddressInfo, Addresses}, - Network, -}; +use crate::utils::address::{AddressInfo, Addresses}; use crate::types::AddressCreateRequest; +use crate::types::Network; #[derive(Debug, Error)] pub enum AddressError { diff --git a/hal-simplicity-daemon/src/daemon/actions/block.rs b/hal-simplicity-daemon/src/daemon/actions/block.rs index 1d74cb5..3649ca0 100644 --- a/hal-simplicity-daemon/src/daemon/actions/block.rs +++ b/hal-simplicity-daemon/src/daemon/actions/block.rs @@ -2,8 +2,9 @@ use elements::encode::{deserialize, serialize}; use elements::{dynafed, Block, BlockExtData, BlockHeader}; use thiserror::Error; +use crate::types::Network; use crate::utils::block::{BlockHeaderInfo, BlockInfo, ParamsInfo, ParamsType}; -use crate::utils::{GetInfo, Network}; +use crate::utils::GetInfo; use crate::types::{BlockCreateRequest, BlockDecodeRequest}; diff --git a/hal-simplicity-daemon/src/daemon/actions/tx.rs b/hal-simplicity-daemon/src/daemon/actions/tx.rs index 6c2402e..9094a5d 100644 --- a/hal-simplicity-daemon/src/daemon/actions/tx.rs +++ b/hal-simplicity-daemon/src/daemon/actions/tx.rs @@ -21,7 +21,8 @@ use crate::utils::tx::{ OutputWitnessInfo, PeginDataInfo, PegoutDataInfo, TransactionInfo, }; -use crate::utils::{GetInfo, Network}; +use crate::types::Network; +use crate::utils::GetInfo; use crate::types::{TxCreateRequest, TxDecodeRequest}; diff --git a/hal-simplicity-daemon/src/lib.rs b/hal-simplicity-daemon/src/lib.rs index d173782..ba06678 100644 --- a/hal-simplicity-daemon/src/lib.rs +++ b/hal-simplicity-daemon/src/lib.rs @@ -3,6 +3,8 @@ pub extern crate simplicity; pub mod jsonrpc; pub mod types; +pub use types::Network; + #[cfg(feature = "daemon")] pub mod daemon; #[cfg(feature = "daemon")] diff --git a/hal-simplicity-daemon/src/types.rs b/hal-simplicity-daemon/src/types.rs index 4a2992a..ecb9c20 100644 --- a/hal-simplicity-daemon/src/types.rs +++ b/hal-simplicity-daemon/src/types.rs @@ -1,3 +1,4 @@ +use elements::AddressParams; use serde::{Deserialize, Deserializer, Serialize, Serializer}; // Re-exports for proper serialization @@ -6,6 +7,37 @@ pub use elements::hashes::sha256; pub use simplicity::bitcoin::secp256k1::schnorr; pub use simplicity::{Amr, Cmr, Ihr}; +/// Known Elements networks. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum Network { + ElementsRegtest, + Liquid, + LiquidTestnet, +} + +impl Network { + pub fn from_params(params: &'static AddressParams) -> Option { + if *params == AddressParams::ELEMENTS { + Some(Network::ElementsRegtest) + } else if *params == AddressParams::LIQUID_TESTNET { + Some(Network::LiquidTestnet) + } else if *params == AddressParams::LIQUID { + Some(Network::Liquid) + } else { + None + } + } + + pub fn address_params(self) -> &'static AddressParams { + match self { + Network::ElementsRegtest => &AddressParams::ELEMENTS, + Network::Liquid => &AddressParams::LIQUID, + Network::LiquidTestnet => &AddressParams::LIQUID_TESTNET, + } + } +} + // Custom serialization for Parity as 0 or 1 mod parity_serde { use super::*; diff --git a/hal-simplicity-daemon/src/utils/mod.rs b/hal-simplicity-daemon/src/utils/mod.rs index ec9e2ee..0374f3d 100644 --- a/hal-simplicity-daemon/src/utils/mod.rs +++ b/hal-simplicity-daemon/src/utils/mod.rs @@ -4,39 +4,7 @@ pub mod confidential; pub mod hal_simplicity; pub mod tx; -use elements::AddressParams; -use serde::{Deserialize, Serialize}; - -/// Known Elements networks. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)] -#[serde(rename_all = "lowercase")] -pub enum Network { - ElementsRegtest, - Liquid, - LiquidTestnet, -} - -impl Network { - pub fn from_params(params: &'static AddressParams) -> Option { - if *params == AddressParams::ELEMENTS { - Some(Network::ElementsRegtest) - } else if *params == AddressParams::LIQUID_TESTNET { - Some(Network::LiquidTestnet) - } else if *params == AddressParams::LIQUID { - Some(Network::Liquid) - } else { - None - } - } - - pub fn address_params(self) -> &'static AddressParams { - match self { - Network::ElementsRegtest => &AddressParams::ELEMENTS, - Network::Liquid => &AddressParams::LIQUID, - Network::LiquidTestnet => &AddressParams::LIQUID_TESTNET, - } - } -} +use crate::types::Network; /// Get JSON-able objects that describe the type. pub trait GetInfo { diff --git a/hal-simplicity-daemon/src/utils/tx.rs b/hal-simplicity-daemon/src/utils/tx.rs index a33a575..e78d790 100644 --- a/hal-simplicity-daemon/src/utils/tx.rs +++ b/hal-simplicity-daemon/src/utils/tx.rs @@ -9,7 +9,8 @@ use serde::{Deserialize, Serialize}; use hal::HexBytes; -use super::{GetInfo, Network}; +use super::GetInfo; +use crate::types::Network; use super::confidential::{ConfidentialAssetInfo, ConfidentialNonceInfo, ConfidentialValueInfo}; From 0a4e85ad84f5c35e3b89a5b5a54c31aa350806fa Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:22:21 +0200 Subject: [PATCH 22/39] reexport simplicity and elements crates from the daemon --- Cargo.lock | 8 -------- hal-simplicity-client/Cargo.toml | 8 -------- hal-simplicity-client/src/hal_simplicity_client.rs | 1 + hal-simplicity-client/src/lib.rs | 2 +- hal-simplicity-client/src/main.rs | 3 ++- hal-simplicity-daemon/src/lib.rs | 1 + 6 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0433566..85eeeeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,24 +472,16 @@ dependencies = [ name = "hal-simplicity-client" version = "0.1.0" dependencies = [ - "chrono", "clap", - "elements", "fern", - "hal", "hal-simplicity-daemon", "hex", - "http-body-util", - "hyper", - "hyper-util", "log", "reqwest", "serde", "serde_json", "serde_yaml", - "simplicity-lang", "thiserror", - "tokio", ] [[package]] diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index b7620c2..3de23c5 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -21,23 +21,15 @@ embed_daemon = ["hal-simplicity-daemon/daemon"] [dependencies] hal-simplicity-daemon = { path = "../hal-simplicity-daemon", default-features = false } -hal = "0.10.0" clap = "2.32" log = "0.4.5" fern = "0.5.6" -chrono = "0.4" serde = { version = "1.0.84", features = [ "derive" ] } serde_json = "1.0.34" serde_yaml = "0.8.8" hex = "0.3.2" -elements = { version = "0.25.2", features = [ "serde", "base64" ] } -simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } -hyper = { version = "1.8.1", features = ["server", "http1"] } -hyper-util = { version = "0.1", features = ["tokio"] } -http-body-util = "0.1" -tokio = { version = "1.48.0", features = ["full"] } thiserror = "2.0.17" reqwest = { version = "0.12.26", features = ["blocking"] } diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs index 68a5c7a..a67be98 100644 --- a/hal-simplicity-client/src/hal_simplicity_client.rs +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -9,6 +9,7 @@ use thiserror::Error; #[cfg(feature = "embed_daemon")] use hal_simplicity_daemon::HalSimplicityDaemon; +#[cfg(feature = "embed_daemon")] const DEFAULT_DAEMON_ADDRESS: &str = "127.0.0.1:28579"; const DEFAULT_DAEMON_URL: &str = "http://localhost:28579"; diff --git a/hal-simplicity-client/src/lib.rs b/hal-simplicity-client/src/lib.rs index 36e6bf2..bb05cdd 100644 --- a/hal-simplicity-client/src/lib.rs +++ b/hal-simplicity-client/src/lib.rs @@ -1,4 +1,4 @@ -pub extern crate simplicity; +pub use hal_simplicity_daemon::{elements, simplicity}; pub mod hal_simplicity_client; diff --git a/hal-simplicity-client/src/main.rs b/hal-simplicity-client/src/main.rs index 6a9262d..ee7772d 100644 --- a/hal-simplicity-client/src/main.rs +++ b/hal-simplicity-client/src/main.rs @@ -1,7 +1,8 @@ use std::panic; use std::process; -pub use elements::bitcoin; +pub use hal_simplicity::elements; +pub use hal_simplicity::elements::bitcoin; pub mod cmd; diff --git a/hal-simplicity-daemon/src/lib.rs b/hal-simplicity-daemon/src/lib.rs index ba06678..e1bcac2 100644 --- a/hal-simplicity-daemon/src/lib.rs +++ b/hal-simplicity-daemon/src/lib.rs @@ -1,3 +1,4 @@ +pub extern crate elements; pub extern crate simplicity; pub mod jsonrpc; From 886e388790b4a3f41549b652de78c7ebc5e1f0f0 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:36:59 +0200 Subject: [PATCH 23/39] Add appropriate return types for basic directives --- hal-simplicity-client/src/cmd/block.rs | 6 ++---- hal-simplicity-client/src/cmd/tx.rs | 6 ++---- .../src/hal_simplicity_client.rs | 14 ++++++------- hal-simplicity-daemon/Cargo.toml | 5 ++--- .../src/daemon/actions/simplicity/info.rs | 2 +- hal-simplicity-daemon/src/daemon/mod.rs | 4 ++-- hal-simplicity-daemon/src/lib.rs | 3 +-- hal-simplicity-daemon/src/types.rs | 20 ++++++++++++++++++- 8 files changed, 36 insertions(+), 24 deletions(-) diff --git a/hal-simplicity-client/src/cmd/block.rs b/hal-simplicity-client/src/cmd/block.rs index 8accba7..345551a 100644 --- a/hal-simplicity-client/src/cmd/block.rs +++ b/hal-simplicity-client/src/cmd/block.rs @@ -35,10 +35,8 @@ fn exec_create<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { let result = client.block_create(block_info, Some(raw_stdout)).expect("failed to create block"); if raw_stdout { - if let Some(raw_hex) = result.as_str() { - let raw_bytes = hex::decode(raw_hex).expect("invalid hex in response"); - ::std::io::stdout().write_all(&raw_bytes).unwrap(); - } + let raw_bytes = hex::decode(&result.raw_block).expect("invalid hex in response"); + ::std::io::stdout().write_all(&raw_bytes).unwrap(); } else { cmd::print_output(matches, &result); } diff --git a/hal-simplicity-client/src/cmd/tx.rs b/hal-simplicity-client/src/cmd/tx.rs index 58b8697..b628a4b 100644 --- a/hal-simplicity-client/src/cmd/tx.rs +++ b/hal-simplicity-client/src/cmd/tx.rs @@ -35,10 +35,8 @@ fn exec_create<'a>(matches: &clap::ArgMatches<'a>, client: &HalSimplicity) { let result = client.tx_create(tx_info, Some(raw_stdout)).expect("failed to create transaction"); if raw_stdout { - if let Some(raw_hex) = result.as_str() { - let raw_bytes = hex::decode(raw_hex).expect("invalid hex in response"); - ::std::io::stdout().write_all(&raw_bytes).unwrap(); - } + let raw_bytes = hex::decode(&result.raw_tx).expect("invalid hex in response"); + ::std::io::stdout().write_all(&raw_bytes).unwrap(); } else { cmd::print_output(matches, &result); } diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs index a67be98..6d924d6 100644 --- a/hal-simplicity-client/src/hal_simplicity_client.rs +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -160,7 +160,7 @@ impl HalSimplicity { pubkey: Option, script: Option, blinder: Option, - ) -> Result { + ) -> Result { let params = AddressCreateRequest { network, pubkey, @@ -172,7 +172,7 @@ impl HalSimplicity { } /// Inspect an Elements address - pub fn address_inspect(&self, address: String) -> Result { + pub fn address_inspect(&self, address: String) -> Result { let params = AddressInspectRequest { address, }; @@ -187,7 +187,7 @@ impl HalSimplicity { &self, block_info: String, raw_stdout: Option, - ) -> Result { + ) -> Result { let params = BlockCreateRequest { block_info, raw_stdout, @@ -202,7 +202,7 @@ impl HalSimplicity { raw_block: String, network: Option, txids: Option, - ) -> Result { + ) -> Result { let params = BlockDecodeRequest { raw_block, network, @@ -219,7 +219,7 @@ impl HalSimplicity { &self, tx_info: String, raw_stdout: Option, - ) -> Result { + ) -> Result { let params = TxCreateRequest { tx_info, raw_stdout, @@ -229,7 +229,7 @@ impl HalSimplicity { } /// Decode a raw transaction - pub fn tx_decode(&self, raw_tx: String, network: Option) -> Result { + pub fn tx_decode(&self, raw_tx: String, network: Option) -> Result { let params = TxDecodeRequest { raw_tx, network, @@ -256,7 +256,7 @@ impl HalSimplicity { witness: Option, state: Option, network: Option, - ) -> Result { + ) -> Result { let params = SimplicityInfoRequest { program, witness, diff --git a/hal-simplicity-daemon/Cargo.toml b/hal-simplicity-daemon/Cargo.toml index 8fac27d..04c94ef 100644 --- a/hal-simplicity-daemon/Cargo.toml +++ b/hal-simplicity-daemon/Cargo.toml @@ -22,7 +22,6 @@ required-features = ["daemon"] [features] default = ["daemon"] daemon = [ - "dep:hal", "dep:clap", "dep:log", "dep:fern", @@ -37,14 +36,14 @@ daemon = [ ] [dependencies] -# Core dependencies +# Core dependencies (always available) serde = { version = "1.0.84", features = [ "derive" ] } serde_json = "1.0.34" elements = { version = "0.25.2", features = [ "serde", "base64" ] } simplicity = { package = "simplicity-lang", version = "0.5.0", features = [ "base64", "serde" ] } +hal = "0.10.0" # Daemon-only dependencies -hal = { version = "0.10.0", optional = true } clap = { version = "2.32", optional = true } log = { version = "0.4.5", optional = true } fern = { version = "0.5.6", optional = true } diff --git a/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs b/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs index 799b2eb..cba0e4f 100644 --- a/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs +++ b/hal-simplicity-daemon/src/daemon/actions/simplicity/info.rs @@ -37,7 +37,7 @@ pub fn info(req: SimplicityInfoRequest) -> Result, } +pub use crate::utils::address::Addresses as AddressCreateResponse; + #[derive(Debug, Serialize, Deserialize)] pub struct AddressInspectRequest { pub address: String, } +pub use crate::utils::address::AddressInfo as AddressInspectResponse; + // Block types #[derive(Debug, Serialize, Deserialize)] pub struct BlockCreateRequest { @@ -80,6 +84,11 @@ pub struct BlockCreateRequest { pub raw_stdout: Option, } +#[derive(Debug, Serialize, Deserialize)] +pub struct BlockCreateResponse { + pub raw_block: String, +} + #[derive(Debug, Serialize, Deserialize)] pub struct BlockDecodeRequest { pub raw_block: String, @@ -87,6 +96,8 @@ pub struct BlockDecodeRequest { pub txids: Option, } +pub use crate::utils::block::BlockInfo as BlockDecodeResponse; + // Transaction types #[derive(Debug, Serialize, Deserialize)] pub struct TxCreateRequest { @@ -94,12 +105,19 @@ pub struct TxCreateRequest { pub raw_stdout: Option, } +#[derive(Debug, Serialize, Deserialize)] +pub struct TxCreateResponse { + pub raw_tx: String, +} + #[derive(Debug, Serialize, Deserialize)] pub struct TxDecodeRequest { pub raw_tx: String, pub network: Option, } +pub use crate::utils::tx::TransactionInfo as TxDecodeResponse; + // Keypair types #[derive(Debug, Serialize, Deserialize)] pub struct KeypairGenerateRequest {} @@ -123,7 +141,7 @@ pub struct SimplicityInfoRequest { #[derive(Debug, Serialize, Deserialize)] pub struct SimplicityInfoResponse { - pub jets: &'static str, + pub jets: String, pub commit_base64: String, pub commit_decode: String, pub type_arrow: String, From 6ea3b46f81f5e828c85b81035818ac0709204aa0 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:37:12 +0200 Subject: [PATCH 24/39] update formatting --- hal-simplicity-client/src/hal_simplicity_client.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hal-simplicity-client/src/hal_simplicity_client.rs b/hal-simplicity-client/src/hal_simplicity_client.rs index 6d924d6..34fb031 100644 --- a/hal-simplicity-client/src/hal_simplicity_client.rs +++ b/hal-simplicity-client/src/hal_simplicity_client.rs @@ -229,7 +229,11 @@ impl HalSimplicity { } /// Decode a raw transaction - pub fn tx_decode(&self, raw_tx: String, network: Option) -> Result { + pub fn tx_decode( + &self, + raw_tx: String, + network: Option, + ) -> Result { let params = TxDecodeRequest { raw_tx, network, From cfb0f439d24430718596dc1dc1e0a63ba242211c Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:38:48 +0200 Subject: [PATCH 25/39] Move tests to the client workspace --- hal-simplicity-client/Cargo.toml | 4 ++++ {tests => hal-simplicity-client/tests}/cli.rs | 0 2 files changed, 4 insertions(+) rename {tests => hal-simplicity-client/tests}/cli.rs (100%) diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index 3de23c5..02d4fdc 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -14,6 +14,10 @@ readme = "README.md" name = "hal_simplicity" path = "src/lib.rs" +[[bin]] +name = "hal-simplicity" +path = "src/main.rs" + [features] default = ["embed_daemon"] embed_daemon = ["hal-simplicity-daemon/daemon"] diff --git a/tests/cli.rs b/hal-simplicity-client/tests/cli.rs similarity index 100% rename from tests/cli.rs rename to hal-simplicity-client/tests/cli.rs From d711d7b5fe645a9295bd5983fa98aa5c14b15455 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:40:00 +0200 Subject: [PATCH 26/39] Flag the client as a default workspace member --- Cargo.toml | 3 +++ hal-simplicity-client/tests/cli.rs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9e5008..4eae9a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,6 @@ members = [ "hal-simplicity-daemon", "hal-simplicity-client" ] +default-members = [ + "hal-simplicity-client" +] diff --git a/hal-simplicity-client/tests/cli.rs b/hal-simplicity-client/tests/cli.rs index 00a5670..248b566 100644 --- a/hal-simplicity-client/tests/cli.rs +++ b/hal-simplicity-client/tests/cli.rs @@ -2,7 +2,7 @@ use std::process::Command; -use elements::hashes::hex::DisplayHex; +use hal_simplicity_daemon::elements::hashes::hex::DisplayHex; fn self_command_str() -> &'static str { env!("CARGO_BIN_EXE_hal-simplicity") @@ -1030,7 +1030,7 @@ FLAGS: // New block to avoid warnings about `struct`s being defined not at the beginning of block { - use elements::bitcoin::secp256k1; + use hal_simplicity_daemon::elements::bitcoin::secp256k1; #[allow(dead_code)] #[derive(serde::Deserialize)] From 2681aa86cfeae17acf1ed2709f1b483c289076fc Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:48:59 +0200 Subject: [PATCH 27/39] update the client version --- Cargo.lock | 2 +- hal-simplicity-client/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85eeeeb..4659514 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "hal-simplicity-client" -version = "0.1.0" +version = "0.1.1" dependencies = [ "clap", "fern", diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index 02d4fdc..997841f 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hal-simplicity-client" -version = "0.1.0" +version = "0.1.1" edition = "2021" authors = ["Steven Roose ", "Andrew Poelstra "] license = "CC0-1.0" From ab7352de3d4970291de4394ca7d8ec7128463296 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 12:49:46 +0200 Subject: [PATCH 28/39] downgrade hal-simplicity-client version to 0.1.0 --- Cargo.lock | 2 +- hal-simplicity-client/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4659514..85eeeeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "hal-simplicity-client" -version = "0.1.1" +version = "0.1.0" dependencies = [ "clap", "fern", diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index 997841f..02d4fdc 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hal-simplicity-client" -version = "0.1.1" +version = "0.1.0" edition = "2021" authors = ["Steven Roose ", "Andrew Poelstra "] license = "CC0-1.0" From e024f92978673a7186bc5b31db9032109f321ba2 Mon Sep 17 00:00:00 2001 From: ivanlele Date: Fri, 19 Dec 2025 15:18:20 +0200 Subject: [PATCH 29/39] Update tests to reflect new CLI outputs --- hal-simplicity-client/Cargo.toml | 5 + hal-simplicity-client/tests/cli.rs | 436 ++++++++++-------- .../src/daemon/actions/address.rs | 3 +- .../src/daemon/actions/block.rs | 8 +- .../src/daemon/actions/tx.rs | 8 +- hal-simplicity-daemon/src/types.rs | 4 +- hal-simplicity-daemon/src/utils/address.rs | 3 +- hal-simplicity-daemon/src/utils/mod.rs | 1 + .../src/utils/serde_helpers.rs | 31 ++ hal-simplicity-daemon/src/utils/tx.rs | 1 + 10 files changed, 301 insertions(+), 199 deletions(-) create mode 100644 hal-simplicity-daemon/src/utils/serde_helpers.rs diff --git a/hal-simplicity-client/Cargo.toml b/hal-simplicity-client/Cargo.toml index 02d4fdc..478df11 100644 --- a/hal-simplicity-client/Cargo.toml +++ b/hal-simplicity-client/Cargo.toml @@ -42,3 +42,8 @@ reqwest = { version = "0.12.26", features = ["blocking"] } needless_question_mark = "allow" # https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 manual_range_contains = "allow" # More readable than clippy's format. uninlined_format_args = "allow" # Stylistic and dumb and inconsistent + +[[test]] +name = "cli" +harness = true +test = true diff --git a/hal-simplicity-client/tests/cli.rs b/hal-simplicity-client/tests/cli.rs index 248b566..d6155dc 100644 --- a/hal-simplicity-client/tests/cli.rs +++ b/hal-simplicity-client/tests/cli.rs @@ -86,13 +86,16 @@ hal-simplicity 0.1.0 hal-simplicity -- a Simplicity-enabled fork of hal USAGE: - hal-simplicity [FLAGS] + hal-simplicity [FLAGS] [OPTIONS] FLAGS: -h, --help Prints help information -V, --version Prints version information -v, --verbose print verbose logging output to stderr +OPTIONS: + --daemon-url URL of hal-simplicity-daemon + SUBCOMMANDS: address work with addresses block manipulate blocks @@ -116,7 +119,7 @@ fn cli_bad_flag() { error: Found argument '-?' which wasn't expected, or isn't valid in this context USAGE: - hal-simplicity [FLAGS] + hal-simplicity [FLAGS] [OPTIONS] For more information try --help ", @@ -130,12 +133,15 @@ hal-simplicity-address 0.1.0 work with addresses USAGE: - hal-simplicity address [FLAGS] + hal-simplicity address [FLAGS] [OPTIONS] FLAGS: -h, --help Prints help information -v, --verbose print verbose logging output to stderr +OPTIONS: + --daemon-url URL of hal-simplicity-daemon + SUBCOMMANDS: create create addresses inspect inspect addresses @@ -163,16 +169,17 @@ FLAGS: -y, --yaml print output in YAML instead of JSON OPTIONS: - --blinder a blinding pubkey in hex - --pubkey a public key in hex - --script