diff --git a/Cargo.lock b/Cargo.lock index d5b92e7..6ebf828 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "async-trait" @@ -119,15 +119,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -150,6 +150,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 = "bech32" version = "0.9.1" @@ -260,9 +266,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -293,9 +299,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.94" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -341,6 +347,25 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -352,9 +377,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -378,9 +403,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -388,9 +413,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", @@ -402,9 +427,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", @@ -418,7 +443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -426,9 +451,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "deranged" @@ -450,6 +475,23 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + [[package]] name = "electrum-client" version = "0.18.0" @@ -576,9 +618,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -597,9 +639,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gloo-utils" @@ -622,9 +664,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hermit-abi" @@ -649,9 +691,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" [[package]] name = "hex_lit" @@ -672,9 +714,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "humantime" @@ -705,6 +747,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -713,12 +873,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -739,7 +901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] @@ -780,15 +942,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -802,15 +970,15 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -834,9 +1002,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -853,9 +1021,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -874,9 +1042,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -884,15 +1052,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -939,9 +1107,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -986,19 +1154,39 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "rayon" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1008,9 +1196,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1019,9 +1207,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ring" @@ -1040,15 +1228,15 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -1068,9 +1256,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -1091,15 +1279,16 @@ dependencies = [ [[package]] name = "sdk_common" version = "0.1.0" +source = "git+https://git.4nkweb.com/4nk/sdk_common.git?branch=dev#3d30eb8ce378e6a142154ee4e1481db7d299c794" dependencies = [ "aes-gcm", "anyhow", + "js-sys", "rand", "serde", "serde_json", - "sp_client 0.1.0 (git+https://github.com/Sosthene00/sp-backend?branch=sp_client)", + "sp_client", "tsify", - "uuid", "wasm-bindgen", ] @@ -1118,7 +1307,6 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sp_client 0.1.0 (git+https://github.com/Sosthene00/sp-client?branch=sp_client)", "tokio", "tokio-stream", "tokio-tungstenite", @@ -1168,18 +1356,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -1188,9 +1376,9 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", @@ -1199,9 +1387,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1210,11 +1398,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.7.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -1228,9 +1416,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.7.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" dependencies = [ "darling", "proc-macro2", @@ -1251,18 +1439,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "silentpayments" -version = "0.1.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3a0169e01dc753fe00f82d0b08a68d49e69f912f592e95ff5ade8b623728a5" +checksum = "658cc05dea6981d226a81e6ce203862274da4d7949482b4eba3575f8f03d714b" dependencies = [ "bech32 0.9.1", "bimap", @@ -1290,9 +1478,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1301,22 +1489,11 @@ dependencies = [ [[package]] name = "sp_client" version = "0.1.0" -source = "git+https://github.com/Sosthene00/sp-backend?branch=sp_client#4eaa51ed306fc939f487c0e14208bf36da8c8a26" -dependencies = [ - "anyhow", - "bitcoin 0.31.2", - "serde", - "serde_json", - "silentpayments", -] - -[[package]] -name = "sp_client" -version = "0.1.0" -source = "git+https://github.com/Sosthene00/sp-client?branch=sp_client#4eaa51ed306fc939f487c0e14208bf36da8c8a26" +source = "git+https://github.com/Sosthene00/sp-client.git?branch=master#211951c64d2d2df6f4c8badac19aed02c03f8551" dependencies = [ "anyhow", "bitcoin 0.31.2", + "rayon", "serde", "serde_json", "silentpayments", @@ -1329,10 +1506,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] -name = "strsim" -version = "0.10.0" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -1342,15 +1525,26 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -1362,18 +1556,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -1412,25 +1606,20 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[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.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -1447,9 +1636,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -1481,9 +1670,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -1541,27 +1730,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "universal-hash" version = "0.5.1" @@ -1580,9 +1754,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -1595,6 +1769,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.8.0" @@ -1602,7 +1788,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", - "serde", ] [[package]] @@ -1718,11 +1903,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1879,6 +2064,63 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeromq" version = "0.3.5" @@ -1905,3 +2147,25 @@ dependencies = [ "tokio-util", "uuid", ] + +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/faucet.rs b/src/faucet.rs new file mode 100644 index 0000000..bba871c --- /dev/null +++ b/src/faucet.rs @@ -0,0 +1,292 @@ +use std::{ + collections::HashMap, + str::FromStr, + sync::{Arc, Mutex}, +}; + +use bitcoincore_rpc::json::{self as bitcoin_json}; +use sdk_common::sp_client::bitcoin::secp256k1::{ + rand::thread_rng, Keypair, Message as Secp256k1Message, Secp256k1, ThirtyTwoByteHash, +}; +use sdk_common::sp_client::bitcoin::{ + absolute::LockTime, + consensus::serialize, + hex::{DisplayHex, FromHex}, + key::TapTweak, + script::PushBytesBuf, + sighash::{Prevouts, SighashCache}, + taproot::Signature, + transaction::Version, + Amount, OutPoint, Psbt, ScriptBuf, TapSighashType, Transaction, TxIn, TxOut, Witness, + XOnlyPublicKey, +}; +use sdk_common::{ + network::{FaucetMessage, NewTxMessage}, + silentpayments::create_transaction_for_address_with_shared_secret, +}; + +use sdk_common::sp_client::silentpayments::sending::generate_recipient_pubkeys; +use sdk_common::sp_client::silentpayments::utils::sending::calculate_partial_secret; +use sdk_common::sp_client::silentpayments::utils::SilentPaymentAddress; +use sdk_common::sp_client::spclient::Recipient; + +use anyhow::{Error, Result}; + +use crate::{ + daemon::Daemon, scan::compute_partial_tweak_to_transaction, MutexExt, SharedDaemon, + SilentPaymentWallet, FAUCET_AMT, +}; + +fn spend_from_core( + dest: XOnlyPublicKey, + daemon: Arc>, +) -> Result<(Transaction, Amount)> { + let core = daemon + .lock() + .map_err(|e| Error::msg(format!("Failed to lock daemon: {}", e.to_string())))?; + let unspent_list: Vec = + core.list_unspent_from_to(None)?; + + if !unspent_list.is_empty() { + let network = core.get_network()?; + + let spk = ScriptBuf::new_p2tr_tweaked(dest.dangerous_assume_tweaked()); + + let new_psbt = core.create_psbt(&unspent_list, spk, network)?; + let processed_psbt = core.process_psbt(new_psbt)?; + let finalize_psbt_result = core.finalize_psbt(processed_psbt)?; + let final_psbt = Psbt::from_str(&finalize_psbt_result)?; + let total_fee = final_psbt.fee()?; + let final_tx = final_psbt.extract_tx()?; + let fee_rate = total_fee + .checked_div(final_tx.weight().to_vbytes_ceil()) + .unwrap(); + + Ok((final_tx, fee_rate)) + } else { + // we don't have enough available coins to pay for this faucet request + Err(Error::msg("No spendable outputs")) + } +} + +fn faucet_send( + sp_address: SilentPaymentAddress, + commitment: &str, + sp_wallet: Arc, + shared_daemon: SharedDaemon, +) -> Result { + let mut first_tx: Option = None; + let final_tx: Transaction; + + // do we have a sp output available ? + let available_outpoints = sp_wallet.get_wallet()?.get_outputs().to_spendable_list(); + + let available_amt = available_outpoints + .iter() + .fold(Amount::from_sat(0), |acc, (_, x)| acc + x.amount); + + // If we don't have at least 4 times the amount we need to send, we take some reserves out + if available_amt > FAUCET_AMT.checked_mul(4).unwrap() { + let mut total_amt = Amount::from_sat(0); + let mut inputs = HashMap::new(); + for (outpoint, output) in available_outpoints { + total_amt += output.amount; + inputs.insert(outpoint, output); + if total_amt >= FAUCET_AMT { + break; + } + } + + let recipient = Recipient { + address: sp_address.into(), + amount: FAUCET_AMT, + nb_outputs: 1, + }; + + let fee_estimate = shared_daemon + .lock_anyhow()? + .estimate_fee(6) + .unwrap_or(Amount::from_sat(1000)) + .checked_div(1000) + .unwrap(); + + log::debug!("fee estimate for 6 blocks: {}", fee_estimate); + + let wallet = sp_wallet.get_wallet()?; + + let signed_psbt = create_transaction_for_address_with_shared_secret( + recipient, + &wallet, + Some(commitment), + fee_estimate, + )?; + + let psbt = Psbt::from_str(&signed_psbt)?; + + final_tx = psbt.extract_tx()?; + } else { + // let's try to spend directly from the mining address + let secp = Secp256k1::signing_only(); + let keypair = Keypair::new(&secp, &mut thread_rng()); + + // we first spend from core to the pubkey we just created + let (core_tx, fee_rate) = + spend_from_core(keypair.x_only_public_key().0, shared_daemon.clone())?; + + // check that the first output of the transaction pays to the key we just created + debug_assert!( + core_tx.output[0].script_pubkey + == ScriptBuf::new_p2tr_tweaked( + keypair.x_only_public_key().0.dangerous_assume_tweaked() + ) + ); + + // This is ugly and can be streamlined + // create a new transaction that spends the newly created UTXO to the sp_address + let mut faucet_tx = Transaction { + input: vec![TxIn { + previous_output: OutPoint::new(core_tx.txid(), 0), + ..Default::default() + }], + output: vec![], + version: Version::TWO, + lock_time: LockTime::ZERO, + }; + + // now do the silent payment operations with the final recipient address + let partial_secret = calculate_partial_secret( + &[(keypair.secret_key(), true)], + &[(core_tx.txid().to_string(), 0)], + )?; + + let ext_output_key: XOnlyPublicKey = + generate_recipient_pubkeys(vec![sp_address.into()], partial_secret)? + .into_values() + .flatten() + .collect::>() + .get(0) + .expect("Failed to generate keys") + .to_owned(); + let change_sp_address = sp_wallet.get_wallet()?.get_client().get_receiving_address(); + let change_output_key: XOnlyPublicKey = + generate_recipient_pubkeys(vec![change_sp_address], partial_secret)? + .into_values() + .flatten() + .collect::>() + .get(0) + .expect("Failed to generate keys") + .to_owned(); + + let ext_spk = ScriptBuf::new_p2tr_tweaked(ext_output_key.dangerous_assume_tweaked()); + let change_spk = ScriptBuf::new_p2tr_tweaked(change_output_key.dangerous_assume_tweaked()); + + let mut op_return = PushBytesBuf::new(); + op_return.extend_from_slice(&Vec::from_hex(commitment)?)?; + let data_spk = ScriptBuf::new_op_return(op_return); + + // Take some margin to pay for the fees + if core_tx.output[0].value < FAUCET_AMT * 4 { + return Err(Error::msg("Not enough funds")); + } + + let change_amt = core_tx.output[0].value.checked_sub(FAUCET_AMT).unwrap(); + + faucet_tx.output.push(TxOut { + value: FAUCET_AMT, + script_pubkey: ext_spk, + }); + faucet_tx.output.push(TxOut { + value: change_amt, + script_pubkey: change_spk, + }); + faucet_tx.output.push(TxOut { + value: Amount::from_sat(0), + script_pubkey: data_spk, + }); + + // dummy signature only used for fee estimation + faucet_tx.input[0].witness.push([1; 64].to_vec()); + + let abs_fee = fee_rate + .checked_mul(faucet_tx.weight().to_vbytes_ceil()) + .ok_or_else(|| Error::msg("Fee rate multiplication overflowed"))?; + + // reset the witness to empty + faucet_tx.input[0].witness = Witness::new(); + + faucet_tx.output[1].value -= abs_fee; + + let first_tx_outputs = vec![core_tx.output[0].clone()]; + let prevouts = Prevouts::All(&first_tx_outputs); + + let hash_ty = TapSighashType::Default; + + let mut cache = SighashCache::new(&faucet_tx); + + let sighash = cache.taproot_key_spend_signature_hash(0, &prevouts, hash_ty)?; + + let msg = Secp256k1Message::from_digest(sighash.into_32()); + + let sig = secp.sign_schnorr_with_rng(&msg, &keypair, &mut thread_rng()); + let final_sig = Signature { sig, hash_ty }; + + faucet_tx.input[0].witness.push(final_sig.to_vec()); + + first_tx = Some(core_tx); + + final_tx = faucet_tx; + } + + if let Ok(daemon) = shared_daemon.lock() { + // broadcast one or two transactions + if first_tx.is_some() { + daemon.broadcast(&first_tx.unwrap())?; + } + let txid = daemon.broadcast(&final_tx)?; + log::debug!("Sent tx {}", txid); + } else { + return Err(Error::msg("Failed to lock daemon")); + } + + Ok(final_tx) +} + +pub fn handle_faucet_request( + msg: &FaucetMessage, + sp_wallet: Arc, + shared_daemon: SharedDaemon, +) -> Result { + let sp_address = SilentPaymentAddress::try_from(msg.sp_address.as_str())?; + log::debug!("Sending bootstrap coins to {}", sp_address); + // send bootstrap coins to this sp_address + let tx = faucet_send( + sp_address, + &msg.commitment, + sp_wallet.clone(), + shared_daemon.clone(), + )?; + + // get the tweak + let partial_tweak = compute_partial_tweak_to_transaction(&tx, shared_daemon.clone())?; + + // get current blockheight + let blkheight: u32 = shared_daemon + .lock_anyhow()? + .get_current_height()? + .try_into()?; + + // update our sp_client with the change output(s) + sp_wallet + .get_wallet()? + .update_wallet_with_transaction(&tx, blkheight, partial_tweak)?; + + log::debug!("updated the wallet"); + // save to disk + sp_wallet.save()?; + + log::debug!("saved the wallet"); + Ok(NewTxMessage::new( + serialize(&tx).to_lower_hex_string(), + Some(partial_tweak.to_string()), + )) +} diff --git a/src/main.rs b/src/main.rs index ee3e922..26a797c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::{ env, fmt::Debug, fs, + io::{Read, Write}, net::SocketAddr, path::PathBuf, str::FromStr, @@ -13,39 +14,23 @@ use std::{ use bitcoincore_rpc::json::{self as bitcoin_json}; use futures_util::{future, pin_mut, stream::TryStreamExt, FutureExt, StreamExt}; use log::{debug, error, info, warn}; +use scan::compute_partial_tweak_to_transaction; use sdk_common::sp_client::bitcoin::{ - absolute::LockTime, consensus::{deserialize, serialize}, hex::{DisplayHex, FromHex}, - key::TapTweak, - script::PushBytesBuf, - sighash::{Prevouts, SighashCache}, - taproot::Signature, - transaction::Version, - Amount, Network, OutPoint, Psbt, ScriptBuf, TapSighashType, Transaction, TxIn, TxOut, Witness, - XOnlyPublicKey, + Amount, Network, Transaction, }; +use sdk_common::sp_client::silentpayments::utils::Network as SpNetwork; use sdk_common::sp_client::{ - bitcoin::secp256k1::{ - rand::{thread_rng, Rng}, - Keypair, Message as Secp256k1Message, PublicKey, Secp256k1, ThirtyTwoByteHash, - }, + bitcoin::secp256k1::rand::{thread_rng, Rng}, spclient::SpWallet, }; use sdk_common::{ error::AnkError, network::{AnkFlag, AnkNetworkMsg, FaucetMessage, NewTxMessage}, - silentpayments::create_transaction_for_address_with_shared_secret, }; -use sdk_common::sp_client::db::{JsonFile, Storage}; -use sdk_common::sp_client::silentpayments::sending::generate_recipient_pubkeys; -use sdk_common::sp_client::silentpayments::utils::SilentPaymentAddress; -use sdk_common::sp_client::silentpayments::utils::receiving::{ - calculate_tweak_data, get_pubkey_from_input, -}; -use sdk_common::sp_client::silentpayments::utils::sending::calculate_partial_secret; -use sdk_common::sp_client::spclient::{derive_keys_from_seed, Recipient, SpClient, SpendKey}; +use sdk_common::sp_client::spclient::{derive_keys_from_seed, SpClient, SpendKey}; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio::{ net::{TcpListener, TcpStream}, @@ -59,8 +44,10 @@ use zeromq::{Socket, SocketRecv}; mod daemon; mod electrumclient; +mod faucet; mod scan; +use crate::faucet::handle_faucet_request; use crate::{daemon::Daemon, scan::scan_blocks}; type Tx = UnboundedSender; @@ -69,6 +56,61 @@ type PeerMap = Arc>>; type SharedDaemon = Arc>; +#[derive(Debug)] +struct WalletFile { + path: PathBuf, +} + +impl WalletFile { + fn new(path: PathBuf) -> Self { + Self { path } + } + + fn create(&self) -> Result<()> { + let parent: PathBuf; + if let Some(dir) = self.path.parent() { + if !dir.ends_with(".4nk") { + return Err(Error::msg("parent dir must be \".4nk\"")); + } + parent = dir.to_path_buf(); + } else { + return Err(Error::msg("wallet file has no parent dir")); + } + + // check that parent exists + if !parent.exists() { + fs::create_dir_all(parent)?; + } + + fs::File::create_new(&self.path)?; + + Ok(()) + } + + fn save(&self, new_value: &SpWallet) -> Result<()> { + let mut f = fs::File::options() + .write(true) + .truncate(true) + .open(&self.path)?; + + let json = serde_json::to_string(new_value)?; + f.write_all(json.as_bytes())?; + + Ok(()) + } + + fn load(&self) -> Result { + let mut f = fs::File::open(&self.path)?; + + let mut content = vec![]; + f.read_to_end(&mut content)?; + + let res: SpWallet = serde_json::from_slice(&content)?; + + Ok(res) + } +} + static MESSAGECACHE: OnceLock = OnceLock::new(); const MESSAGECACHEDURATION: Duration = Duration::from_secs(10); @@ -95,36 +137,36 @@ impl MessageCache { let store = self.store.lock().unwrap(); store.contains_key(key) } -} -async fn clean_up() { - let cache = MESSAGECACHE.get().unwrap(); + async fn clean_up() { + let cache = MESSAGECACHE.get().unwrap(); - let mut interval = time::interval(MESSAGECACHEINTERVAL); + let mut interval = time::interval(MESSAGECACHEINTERVAL); - loop { - interval.tick().await; + loop { + interval.tick().await; - let mut store = cache.store.lock().unwrap(); + let mut store = cache.store.lock().unwrap(); - let now = Instant::now(); - let to_rm: Vec = store - .iter() - .filter_map(|(entry, entrytime)| { - if let Some(duration) = now.checked_duration_since(*entrytime) { - if duration > MESSAGECACHEDURATION { - Some(entry.clone()) + let now = Instant::now(); + let to_rm: Vec = store + .iter() + .filter_map(|(entry, entrytime)| { + if let Some(duration) = now.checked_duration_since(*entrytime) { + if duration > MESSAGECACHEDURATION { + Some(entry.clone()) + } else { + None + } } else { None } - } else { - None - } - }) - .collect(); + }) + .collect(); - for key in to_rm { - store.remove(&key); + for key in to_rm { + store.remove(&key); + } } } } @@ -145,7 +187,7 @@ impl MutexExt for Mutex { #[derive(Debug)] struct SilentPaymentWallet { sp_wallet: Mutex, - storage: Mutex, + storage: Mutex, } impl SilentPaymentWallet { @@ -154,7 +196,8 @@ impl SilentPaymentWallet { } pub fn save(&self) -> Result<()> { - self.storage.lock_anyhow()?.save(&self.sp_wallet) + let wallet = self.sp_wallet.lock_anyhow()?; + self.storage.lock_anyhow()?.save(&wallet) } } @@ -212,262 +255,6 @@ fn broadcast_message( Ok(()) } -fn spend_from_core( - dest: XOnlyPublicKey, - daemon: Arc>, -) -> Result<(Transaction, Amount)> { - let core = daemon - .lock() - .map_err(|e| Error::msg(format!("Failed to lock daemon: {}", e.to_string())))?; - let unspent_list: Vec = - core.list_unspent_from_to(None)?; - - if !unspent_list.is_empty() { - let network = core.get_network()?; - - let spk = ScriptBuf::new_p2tr_tweaked(dest.dangerous_assume_tweaked()); - - let new_psbt = core.create_psbt(&unspent_list, spk, network)?; - let processed_psbt = core.process_psbt(new_psbt)?; - let finalize_psbt_result = core.finalize_psbt(processed_psbt)?; - let final_psbt = Psbt::from_str(&finalize_psbt_result)?; - let total_fee = final_psbt.fee()?; - let final_tx = final_psbt.extract_tx()?; - let fee_rate = total_fee - .checked_div(final_tx.weight().to_vbytes_ceil()) - .unwrap(); - - Ok((final_tx, fee_rate)) - } else { - // we don't have enough available coins to pay for this faucet request - Err(Error::msg("No spendable outputs")) - } -} - -fn faucet_send( - sp_address: SilentPaymentAddress, - commitment: &str, - sp_wallet: Arc, - shared_daemon: SharedDaemon, -) -> Result { - let mut first_tx: Option = None; - let final_tx: Transaction; - - // do we have a sp output available ? - let available_outpoints = sp_wallet.get_wallet()?.get_outputs().to_spendable_list(); - - let available_amt = available_outpoints - .iter() - .fold(Amount::from_sat(0), |acc, (_, x)| acc + x.amount); - - // If we don't have at least 4 times the amount we need to send, we take some reserves out - if available_amt > FAUCET_AMT.checked_mul(4).unwrap() { - let mut total_amt = Amount::from_sat(0); - let mut inputs = HashMap::new(); - for (outpoint, output) in available_outpoints { - total_amt += output.amount; - inputs.insert(outpoint, output); - if total_amt >= FAUCET_AMT { - break; - } - } - - let recipient = Recipient { - address: sp_address.into(), - amount: FAUCET_AMT, - nb_outputs: 1, - }; - - let fee_estimate = shared_daemon - .lock_anyhow()? - .estimate_fee(6) - .unwrap_or(Amount::from_sat(1000)) - .checked_div(1000) - .unwrap(); - - log::debug!("fee estimate for 6 blocks: {}", fee_estimate); - - let wallet = sp_wallet.get_wallet()?; - - let signed_psbt = create_transaction_for_address_with_shared_secret( - recipient, - &wallet, - Some(commitment), - fee_estimate, - )?; - - let psbt = Psbt::from_str(&signed_psbt)?; - - final_tx = psbt.extract_tx()?; - } else { - // let's try to spend directly from the mining address - let secp = Secp256k1::signing_only(); - let keypair = Keypair::new(&secp, &mut thread_rng()); - - // we first spend from core to the pubkey we just created - let (core_tx, fee_rate) = - spend_from_core(keypair.x_only_public_key().0, shared_daemon.clone())?; - - // check that the first output of the transaction pays to the key we just created - debug_assert!( - core_tx.output[0].script_pubkey - == ScriptBuf::new_p2tr_tweaked( - keypair.x_only_public_key().0.dangerous_assume_tweaked() - ) - ); - - // This is ugly and can be streamlined - // create a new transaction that spends the newly created UTXO to the sp_address - let mut faucet_tx = Transaction { - input: vec![TxIn { - previous_output: OutPoint::new(core_tx.txid(), 0), - ..Default::default() - }], - output: vec![], - version: Version::TWO, - lock_time: LockTime::ZERO, - }; - - // now do the silent payment operations with the final recipient address - let partial_secret = calculate_partial_secret( - &[(keypair.secret_key(), true)], - &[(core_tx.txid().to_string(), 0)], - )?; - - let ext_output_key: XOnlyPublicKey = - generate_recipient_pubkeys(vec![sp_address.into()], partial_secret)? - .into_values() - .flatten() - .collect::>() - .get(0) - .expect("Failed to generate keys") - .to_owned(); - let change_sp_address = sp_wallet.get_wallet()?.get_client().get_receiving_address(); - let change_output_key: XOnlyPublicKey = - generate_recipient_pubkeys(vec![change_sp_address], partial_secret)? - .into_values() - .flatten() - .collect::>() - .get(0) - .expect("Failed to generate keys") - .to_owned(); - - let ext_spk = ScriptBuf::new_p2tr_tweaked(ext_output_key.dangerous_assume_tweaked()); - let change_spk = ScriptBuf::new_p2tr_tweaked(change_output_key.dangerous_assume_tweaked()); - - let mut op_return = PushBytesBuf::new(); - op_return.extend_from_slice(&Vec::from_hex(commitment)?)?; - let data_spk = ScriptBuf::new_op_return(op_return); - - // Take some margin to pay for the fees - if core_tx.output[0].value < FAUCET_AMT * 4 { - return Err(Error::msg("Not enough funds")); - } - - let change_amt = core_tx.output[0].value.checked_sub(FAUCET_AMT).unwrap(); - - faucet_tx.output.push(TxOut { - value: FAUCET_AMT, - script_pubkey: ext_spk, - }); - faucet_tx.output.push(TxOut { - value: change_amt, - script_pubkey: change_spk, - }); - faucet_tx.output.push(TxOut { - value: Amount::from_sat(0), - script_pubkey: data_spk, - }); - - // dummy signature only used for fee estimation - faucet_tx.input[0].witness.push([1; 64].to_vec()); - - let abs_fee = fee_rate - .checked_mul(faucet_tx.weight().to_vbytes_ceil()) - .ok_or_else(|| Error::msg("Fee rate multiplication overflowed"))?; - - // reset the witness to empty - faucet_tx.input[0].witness = Witness::new(); - - faucet_tx.output[1].value -= abs_fee; - - let first_tx_outputs = vec![core_tx.output[0].clone()]; - let prevouts = Prevouts::All(&first_tx_outputs); - - let hash_ty = TapSighashType::Default; - - let mut cache = SighashCache::new(&faucet_tx); - - let sighash = cache.taproot_key_spend_signature_hash(0, &prevouts, hash_ty)?; - - let msg = Secp256k1Message::from_digest(sighash.into_32()); - - let sig = secp.sign_schnorr_with_rng(&msg, &keypair, &mut thread_rng()); - let final_sig = Signature { sig, hash_ty }; - - faucet_tx.input[0].witness.push(final_sig.to_vec()); - - first_tx = Some(core_tx); - - final_tx = faucet_tx; - } - - if let Ok(daemon) = shared_daemon.lock() { - // broadcast one or two transactions - if first_tx.is_some() { - daemon.broadcast(&first_tx.unwrap())?; - } - let txid = daemon.broadcast(&final_tx)?; - debug!("Sent tx {}", txid); - } else { - return Err(Error::msg("Failed to lock daemon")); - } - - Ok(final_tx) -} - -fn handle_faucet_request( - msg: &FaucetMessage, - sp_wallet: Arc, - shared_daemon: SharedDaemon, -) -> Result { - let sp_address = SilentPaymentAddress::try_from(msg.sp_address.as_str())?; - debug!("Sending bootstrap coins to {}", sp_address); - // send bootstrap coins to this sp_address - let tx = faucet_send( - sp_address, - &msg.commitment, - sp_wallet.clone(), - shared_daemon.clone(), - )?; - - // get the tweak - let partial_tweak = compute_partial_tweak_to_transaction(&tx, shared_daemon.clone())?; - - // get current blockheight - let blkheight: u32 = shared_daemon - .lock_anyhow()? - .get_current_height()? - .try_into()?; - - // update our sp_client with the change output(s) - sp_wallet - .get_wallet()? - .update_wallet_with_transaction(&tx, blkheight, partial_tweak)?; - - debug!("{:?}", sp_wallet); - - debug!("updated the wallet"); - // save to disk - sp_wallet.save()?; - - debug!("saved the wallet"); - Ok(NewTxMessage::new( - serialize(&tx).to_lower_hex_string(), - Some(partial_tweak.to_string()), - )) -} - fn handle_new_tx_request(new_tx_msg: &mut NewTxMessage, shared_daemon: SharedDaemon) -> Result<()> { let tx = deserialize::(&Vec::from_hex(&new_tx_msg.transaction)?)?; let mempool_accept = shared_daemon.lock_anyhow()?.test_mempool_accept(&tx)?; @@ -646,47 +433,6 @@ async fn handle_connection( peers.lock().unwrap().remove(&addr); } -fn compute_partial_tweak_to_transaction( - tx: &Transaction, - daemon: Arc>, -) -> Result { - let mut outpoints: Vec<(String, u32)> = Vec::with_capacity(tx.input.len()); - let mut pubkeys: Vec = Vec::with_capacity(tx.input.len()); - for input in tx.input.iter() { - outpoints.push(( - input.previous_output.txid.to_string(), - input.previous_output.vout, - )); - let prev_tx = daemon - .lock_anyhow()? - .get_transaction(&input.previous_output.txid, None) - .map_err(|e| Error::msg(format!("Failed to find previous transaction: {}", e)))?; - - if let Some(output) = prev_tx.output.get(input.previous_output.vout as usize) { - match get_pubkey_from_input( - &input.script_sig.to_bytes(), - &input.witness.to_vec(), - &output.script_pubkey.to_bytes(), - ) { - Ok(Some(pubkey)) => pubkeys.push(pubkey), - Ok(None) => continue, - Err(e) => { - return Err(Error::msg(format!( - "Can't extract pubkey from input: {}", - e - ))) - } - } - } else { - return Err(Error::msg("Transaction with a non-existing input")); - } - } - - let input_pub_keys: Vec<&PublicKey> = pubkeys.iter().collect(); - let partial_tweak = calculate_tweak_data(&input_pub_keys, &outpoints)?; - Ok(partial_tweak) -} - fn create_new_tx_message(transaction: Vec, daemon: Arc>) -> Result { // debug!("Creating tx message"); let tx: Transaction = deserialize(&transaction)?; @@ -767,7 +513,15 @@ async fn main() -> Result<()> { let network_arg: String = env::args().nth(4).unwrap_or_else(|| "signet".to_owned()); let core_wallet: Option = env::args().nth(5); - let network = Network::from_core_arg(&network_arg)?; + let network: Network = match network_arg.as_str() { + "main" => Network::Bitcoin, + "test" => Network::Testnet, + "signet" => Network::Testnet, + "regtest" => Network::Regtest, + _ => return Err(Error::msg("Invalid network")), + }; + + // let network = Network::from_core_arg(&network_arg)?; if network == Network::Bitcoin { warn!("Running on mainnet, you're on your own"); @@ -777,12 +531,14 @@ async fn main() -> Result<()> { .set(MessageCache::new()) .expect("Message Cache initialization failed"); - tokio::spawn(clean_up()); - let peers = PeerMap::new(Mutex::new(HashMap::new())); // Connect the rpc daemon - let shared_daemon = Arc::new(Mutex::new(Daemon::connect(core_wallet, rpc_url, network)?)); + let shared_daemon = Arc::new(Mutex::new(Daemon::connect( + core_wallet, + rpc_url, + Network::from_core_arg(&network_arg)?, + )?)); let current_tip: u32 = shared_daemon .lock_anyhow()? @@ -791,27 +547,24 @@ async fn main() -> Result<()> { let mut config_dir = PathBuf::from_str(&env::var("HOME")?)?; config_dir.push(".4nk"); - let sp_wallet_file = JsonFile::new(&config_dir, &PathBuf::from_str(&wallet_name)?); - fs::create_dir_all(config_dir)?; + config_dir.push(&wallet_name); + + let wallet_file = WalletFile::new(config_dir); // load an existing sp_wallet, or create a new one - let is_testnet = if network == Network::Bitcoin { - false - } else { - true - }; - let sp_wallet = match >::load(&sp_wallet_file) { + let sp_wallet = match wallet_file.load() { Err(_) => { + wallet_file.create()?; let mut seed = [0u8; 64]; thread_rng().fill(&mut seed); - let (scan_sk, spend_sk) = derive_keys_from_seed(&seed, is_testnet) - .expect("Couldn't generate a new sp_wallet"); + let (scan_sk, spend_sk) = + derive_keys_from_seed(&seed, network).expect("Couldn't generate a new sp_wallet"); let new_client = SpClient::new( wallet_name, scan_sk, SpendKey::Secret(spend_sk), None, - is_testnet, + network, ) .expect("Failed to create a new SpClient"); @@ -842,7 +595,7 @@ async fn main() -> Result<()> { let last_scan = sp_wallet.get_outputs().get_last_scan(); let shared_sp_wallet = Mutex::new(sp_wallet); - let shared_wallet_storage = Mutex::new(sp_wallet_file); + let shared_wallet_storage = Mutex::new(wallet_file); let sp_wallet = Arc::new(SilentPaymentWallet { sp_wallet: shared_sp_wallet, @@ -868,6 +621,8 @@ async fn main() -> Result<()> { let listener = try_socket.expect("Failed to bind"); debug!("Listening on: {}", listening_addr); + tokio::spawn(MessageCache::clean_up()); + // Let's spawn the handling of each connection in a separate task. while let Ok((stream, addr)) = listener.accept().await { tokio::spawn(handle_connection( diff --git a/src/scan.rs b/src/scan.rs index aac69ca..56a53b1 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use anyhow::{Error, Result}; use electrum_client::ElectrumApi; @@ -10,11 +10,56 @@ use sdk_common::sp_client::bitcoin::hex::DisplayHex; use sdk_common::sp_client::bitcoin::secp256k1::{All, PublicKey, Scalar, Secp256k1, SecretKey}; use sdk_common::sp_client::bitcoin::{BlockHash, OutPoint, Transaction, TxOut, XOnlyPublicKey}; use sdk_common::sp_client::silentpayments::receiving::Receiver; +use sdk_common::sp_client::silentpayments::utils::receiving::{ + calculate_tweak_data, get_pubkey_from_input, +}; use sdk_common::sp_client::spclient::{OutputSpendStatus, OwnedOutput}; use tokio::time::Instant; +use crate::daemon::Daemon; use crate::{electrumclient, MutexExt, SharedDaemon, SilentPaymentWallet}; +pub fn compute_partial_tweak_to_transaction( + tx: &Transaction, + daemon: Arc>, +) -> Result { + let mut outpoints: Vec<(String, u32)> = Vec::with_capacity(tx.input.len()); + let mut pubkeys: Vec = Vec::with_capacity(tx.input.len()); + for input in tx.input.iter() { + outpoints.push(( + input.previous_output.txid.to_string(), + input.previous_output.vout, + )); + let prev_tx = daemon + .lock_anyhow()? + .get_transaction(&input.previous_output.txid, None) + .map_err(|e| Error::msg(format!("Failed to find previous transaction: {}", e)))?; + + if let Some(output) = prev_tx.output.get(input.previous_output.vout as usize) { + match get_pubkey_from_input( + &input.script_sig.to_bytes(), + &input.witness.to_vec(), + &output.script_pubkey.to_bytes(), + ) { + Ok(Some(pubkey)) => pubkeys.push(pubkey), + Ok(None) => continue, + Err(e) => { + return Err(Error::msg(format!( + "Can't extract pubkey from input: {}", + e + ))) + } + } + } else { + return Err(Error::msg("Transaction with a non-existing input")); + } + } + + let input_pub_keys: Vec<&PublicKey> = pubkeys.iter().collect(); + let partial_tweak = calculate_tweak_data(&input_pub_keys, &outpoints)?; + Ok(partial_tweak) +} + fn get_script_to_secret_map( sp_receiver: &Receiver, tweak_data_vec: Vec,