commit 3ce9bc60197300743e2b036b82187417338fb92a Author: waalge Date: Tue Sep 30 14:42:30 2025 +0000 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34d718f --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +db/ +data/ +tmp/ + +result + +secrets/ +.direnv/ +target/ + +.env + +# Added by cargo + +/target + diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ab00051 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3055 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[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 = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.9.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" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blockfrost" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3f18d436df2ac619d7cfa04f883b5f241619d3ef43c893d3c035012a271f81" +dependencies = [ + "blockfrost-openapi", + "futures", + "futures-timer", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.16", + "url", +] + +[[package]] +name = "blockfrost-openapi" +version = "0.1.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e88a3c131d5e95b82a761d9b4ae0d100b67b138a9fd1d880742b50b26318a5" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "uuid", +] + +[[package]] +name = "blst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.0", +] + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[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 = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cryptoxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" + +[[package]] +name = "cryptoxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "facfae029ec4373769eb4bd936bcf537de1052abaee9f246e667c9443be6aa95" + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[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 = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[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.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[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 = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.11.4", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hamming" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "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 = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[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 = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown 0.16.0", + "serde", + "serde_core", +] + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "konduit-cli" +version = "0.0.0" +dependencies = [ + "anyhow", + "bech32 0.11.0", + "blockfrost", + "blockfrost-openapi", + "clap", + "color-print", + "cryptoxide 0.5.1", + "hex", + "indoc", + "minicbor", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "rand", + "reqwest", + "serde", + "serde_json", + "tokio", + "uplc", +] + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror 1.0.69", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minicbor" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" +dependencies = [ + "half", + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[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 = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "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.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pallas-addresses" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f5f4dd205316335bf8eef77227e01a8a00b1fd60503d807520e93dd0362d0e" +dependencies = [ + "base58", + "bech32 0.9.1", + "crc", + "cryptoxide 0.4.4", + "hex", + "pallas-codec", + "pallas-crypto", + "thiserror 1.0.69", +] + +[[package]] +name = "pallas-codec" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2737b05f0dbb6d197feeb26ef15d2567e54833184bd469f5655a0537da89fa" +dependencies = [ + "hex", + "minicbor", + "num-bigint", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "pallas-crypto" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0368945cd093e550febe36aef085431b1611c2e9196297cd70f4b21a4add054c" +dependencies = [ + "cryptoxide 0.4.4", + "hex", + "pallas-codec", + "rand_core 0.6.4", + "serde", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "pallas-primitives" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb2acde8875c43446194d387c60fe2d6a127e4f8384bef3dcabd5a04e9422429" +dependencies = [ + "base58", + "bech32 0.9.1", + "hex", + "log", + "pallas-codec", + "pallas-crypto", + "serde", + "serde_json", +] + +[[package]] +name = "pallas-traverse" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab64895a0d94fed1ef2d99dd37e480ed0483e91eb98dcd2f94cc614fb9575173" +dependencies = [ + "hex", + "itertools 0.13.0", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "paste", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "peg" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" + +[[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" +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 = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f3aa1e3ca87d3b124db7461265ac176b40c277f37e503eaa29c9c75c037846" +dependencies = [ + "arrayvec", + "log", + "typed-arena", + "unicode-segmentation", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[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.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.0", +] + +[[package]] +name = "rustls" +version = "0.23.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.0", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4473013577ec77b4ee3668179ef1186df3146e2cf2d927bd200974c6fe60fd99" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "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.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[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_with" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.11.4", + "schemars 0.9.0", + "schemars 1.0.4", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[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.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[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.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.1", + "once_cell", + "rustix", + "windows-sys 0.61.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "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-rustls" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +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 = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +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 = "uplc" +version = "1.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca1fd5d08135f05ad3e19b72f0596c908c66d4879002ae4fa02ff36959338e2a" +dependencies = [ + "bitvec", + "blst", + "cryptoxide 0.4.4", + "hamming", + "hex", + "indexmap 1.9.3", + "itertools 0.10.5", + "k256", + "miette", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "pallas-traverse", + "peg", + "pretty", + "secp256k1", + "serde", + "serde_json", + "strum", + "thiserror 1.0.69", +] + +[[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 = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.1", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[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.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.0", + "windows-result 0.4.0", + "windows-strings 0.5.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "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 = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..aa6c910 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "konduit-cli" +version = "0.0.0" +edition = "2021" +publish = false +rust-version = "1.86.0" +# license = "MIT" -- set to apache + +[package.metadata.release] +release = false + +[dependencies] +anyhow = "1.0.100" +bech32 = "0.11.0" +blockfrost = "1.1.0" +blockfrost-openapi = "0.1.75" +clap = { version = "4.5.18", features = ["cargo", "derive"] } +cryptoxide = "0.5.1" +hex = "0.4.3" +minicbor = { version = "0.25.1", features = ["alloc", "derive"] } +pallas-addresses = "0.33.0" +pallas-crypto = "0.33.0" +pallas-primitives = "0.33.0" +rand = "0.9.2" +reqwest = { version = "0.12.23", features = ["json"] } +serde = { version = "1.0.213", features = ["derive"] } +serde_json = "1.0.138" +tokio = { version = "1.47.1", features = ["full"] } +uplc = "1.1.19" diff --git a/README.md b/README.md new file mode 100644 index 0000000..bbe7764 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Konduit cli + +> A first stab at the konduit cli + +## TODOs + +- [x] serde for relevant data +- [ ] env + - [ ] wallet keys + - [ ] cardano connection +- [ ] txs + - [ ] dev + - [ ] send + - [ ] publish + - [ ] unpublish + - [ ] open + - [ ] cli params TODO + - [ ] fn params: + - fuel (available utxos) + - change address + - konduit address : + - `konduit_hash` + - maybe stake key + - amount (currency is always ada) + - datum: + - constants`(tag, add_vkey, sub_vkey, respond_period)` + - [ ] add + - [ ] sub + - [ ] cli params TODO + - [ ] fn args: + - generic script args: fuel, change address, script ref ... + - instance input (resolved) + - redeemer: receipt = (squash, [cheques]) + - [ ] close + - [ ] respond + - [ ] unlock + - [ ] expire + - [ ] end + - [ ] elapse + - [ ] batch + - [ ] mutual +- [ ] cardano connection + - [ ] api + - [ ] utxos at (with optional stake key) + - implementations: + - [ ] blockfrost + - [ ] kupmios +- [ ] env handling +- [ ] cmd diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..cea1e2a --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1757068644, + "narHash": "sha256-NOrUtIhTkIIumj1E/Rsv1J37Yi3xGStISEo8tZm3KW4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "8eb28adfa3dc4de28e792e3bf49fcf9007ca8ac9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1757298987, + "narHash": "sha256-yuFSw6fpfjPtVMmym51ozHYpJQ7SzVOTkk7tUv2JA0U=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "cfd63776bde44438ff2936f0c9194c79dd407a5f", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..50518c4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,91 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + rust-overlay, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [rust-overlay.overlays.default]; + }; + + osxDependencies = with pkgs; + lib.optionals stdenv.isDarwin + [ + darwin.apple_sdk.frameworks.Security + darwin.apple_sdk.frameworks.CoreServices + darwin.apple_sdk.frameworks.SystemConfiguration + ]; + + cargoTomlContents = builtins.readFile ./Cargo.toml; + + version = (builtins.fromTOML cargoTomlContents).package.version; + rustVersion = (builtins.fromTOML cargoTomlContents).package."rust-version"; + + rustToolchain = pkgs.rust-bin.stable.${rustVersion}.default; + + rustPlatform = pkgs.makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; + }; + + my-crate = rustPlatform.buildRustPackage { + inherit version; + + name = "my-crate"; + + buildInputs = with pkgs; [openssl] ++ osxDependencies; + nativeBuildInputs = with pkgs; [pkg-config openssl.dev]; + + src = pkgs.lib.cleanSourceWith {src = self;}; + doCheck = false; # don’t run cargo test + CARGO_BUILD_TESTS = "false"; # don’t even compile test binaries + + cargoLock.lockFile = ./Cargo.lock; + + GIT_COMMIT_HASH_SHORT = self.shortRev or "unknown"; + }; + + packages = { + my-crate = my-crate; + default = packages.my-crate; + }; + + overlays.default = final: prev: {my-crate = packages.my-crate;}; + + gitRev = + if (builtins.hasAttr "rev" self) + then self.rev + else "dirty"; + in { + inherit packages overlays; + + devShell = pkgs.mkShell { + buildInputs = with pkgs; + [ + pkg-config + openssl + cargo-insta + (rustToolchain.override { + extensions = ["rust-src" "clippy" "rustfmt" "rust-analyzer"]; + }) + ] + ++ osxDependencies; + + shellHook = '' + export GIT_REVISION=${gitRev} + ''; + }; + }); +} diff --git a/src/cardano.rs b/src/cardano.rs new file mode 100644 index 0000000..59f751a --- /dev/null +++ b/src/cardano.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; + +use cardano::Cardano; + +pub mod blockfrost; +pub mod cardano; + +const PREFIX: &str = "cardano_"; + +pub fn from_env(env: &HashMap) -> impl Cardano { + let cardano_env: HashMap = env + .iter() + .filter_map(|(k, v)| k.strip_prefix(PREFIX).map(|k| (k.to_string(), v.clone()))) + .collect(); + match env.get("cardano") { + None => panic!("Expect cardano connection details in env"), + Some(s) if s == "blockfrost" => { + let config = blockfrost::Config::from_env(&cardano_env); + blockfrost::Blockfrost::new(config.project_id) + } + Some(_s) => panic!("Unkown cardano connection"), + } +} diff --git a/src/cardano/blockfrost.rs b/src/cardano/blockfrost.rs new file mode 100644 index 0000000..828d0fb --- /dev/null +++ b/src/cardano/blockfrost.rs @@ -0,0 +1,458 @@ +// ORIGINAL SOURCE : https://github.com/CardanoSolutions/zhuli/blob/main/cli/src/cardano.rs + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use anyhow::{anyhow, Result}; +use minicbor::decode; + +use crate::{tx::plutus::BuildParams, utils::v2a}; +use blockfrost::{BlockfrostAPI, Pagination}; +use blockfrost_openapi::models::{ + asset_history_inner::Action, tx_content_output_amount_inner::TxContentOutputAmountInner, +}; +use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart}; +use pallas_codec::{ + minicbor as cbor, + utils::{CborWrap, NonEmptyKeyValuePairs}, +}; +use pallas_primitives::{ + conway::{ + AssetName, DatumOption, PolicyId, PostAlonzoTransactionOutput, PseudoDatumOption, + ScriptRef, TransactionInput, TransactionOutput, Tx, Value, + }, + PlutusScript, +}; +use std::collections::{BTreeMap, HashMap}; +use uplc::{tx::ResolvedInput, PlutusData}; + +use super::cardano::Cardano; + +pub struct Config { + pub project_id: String, +} + +impl Config { + pub fn from_env(env: &HashMap) -> Self { + let project_id = env + .get("project_id") + .expect("Blockfrost requires `project_id`") + .clone(); + Self { project_id } + } +} + +pub struct Blockfrost { + api: BlockfrostAPI, + base_url: String, + client: reqwest::Client, + network: Network, + project_id: String, +} + +const UNIT_LOVELACE: &str = "lovelace"; + +const MAINNET_PREFIX: &str = "mainnet"; +const PREPROD_PREFIX: &str = "preprod"; +const PREVIEW_PREFIX: &str = "preview"; + +#[derive(Debug)] +pub struct ProtocolParameters { + pub collateral_percent: f64, + pub cost_model_v3: Vec, + pub drep_deposit: u64, + pub fee_constant: u64, + pub fee_coefficient: u64, + pub min_utxo_deposit_coefficient: u64, + pub price_mem: f64, + pub price_steps: f64, +} + +impl From<&ProtocolParameters> for BuildParams { + fn from(params: &ProtocolParameters) -> BuildParams { + BuildParams { + fee_constant: params.fee_constant, + fee_coefficient: params.fee_coefficient, + price_mem: params.price_mem, + price_steps: params.price_steps, + } + } +} + +impl Blockfrost { + pub fn new(project_id: String) -> Self { + let network_prefix = if project_id.starts_with(MAINNET_PREFIX) { + MAINNET_PREFIX.to_string() + } else if project_id.starts_with(PREPROD_PREFIX) { + PREPROD_PREFIX.to_string() + } else if project_id.starts_with(PREVIEW_PREFIX) { + PREVIEW_PREFIX.to_string() + } else { + panic!("unexpected project id prefix") + }; + let base_url = format!("https://cardano-{}.blockfrost.io/api/v0", network_prefix,); + let api = BlockfrostAPI::new(project_id.as_str(), Default::default()); + Self { + api, + base_url, + client: reqwest::Client::new(), + network: if project_id.starts_with(MAINNET_PREFIX) { + Network::Mainnet + } else { + Network::Testnet + }, + project_id, + } + } + + pub async fn minting(&self, policy_id: &PolicyId, asset_name: &AssetName) -> Vec { + let history = self + .api + .assets_history( + &format!("{}{}", hex::encode(policy_id), hex::encode(&asset_name[..])), + Pagination::all(), + ) + .await + .ok() + .unwrap_or(vec![]) + .into_iter() + .filter_map(|inner| { + if matches!(inner.action, Action::Minted) { + Some(inner.tx_hash) + } else { + None + } + }) + .collect::>(); + + let mut txs: Vec = vec![]; + for tx_hash in history { + if let Ok(tx) = self.transaction_by_hash(&tx_hash).await { + txs.push(tx) + } + } + txs + } + + pub async fn plutus_data_from_hash(&self, datum_hash: &str) -> Result { + let x = self.api.scripts_datum_hash_cbor(datum_hash).await?; + let data = x + .as_object() + .expect("Expect an object") + .get("cbor") + .expect("Expect key `cbor`") + .as_str() + .expect("Expect value to be string"); + plutus_data_from_inline(&data) + } + + pub async fn resolve_datum_option( + &self, + datum_hash: &Option, + inline_datum: &Option, + ) -> Result> { + if let Some(inline_datum) = inline_datum { + Ok(Some(PseudoDatumOption::Data(CborWrap( + plutus_data_from_inline(inline_datum)?, + )))) + } else { + if let Some(datum_hash) = datum_hash { + Ok(Some(PseudoDatumOption::Data(CborWrap( + self.plutus_data_from_hash(&datum_hash).await?, + )))) + } else { + Ok(None) + } + } + } + + /// Blockfrost client has the wrong type. + pub async fn scripts_hash_cbor(&self, script_hash: &str) -> Result> { + let response = self + .client + .get(&format!("{}/scripts/{}", self.base_url, script_hash)) + .header("Accept", "application/json") + .header("project_id", self.project_id.as_str()) + .send() + .await + .unwrap(); + + match response.status() { + reqwest::StatusCode::OK => { + let ResponseCbor { cbor } = response.json::().await.unwrap(); + let bytes = hex::decode(cbor)?; + Ok(bytes) + } + _ => Err(anyhow!("No script found")), + } + } + + // /// Blockfrost client has incomplete type + // pub async fn script_type(&self, script_hash : &str) -> Result{ + // let response = self + // .client + // .get(&format!( "{}/scripts/{}/cbor", self.base_url, script_hash)) + // .header("Accept", "application/json") + // .header("project_id", self.project_id.as_str()) + // .send() + // .await + // .unwrap(); + + // match response.status() { + // reqwest::StatusCode::OK => { + // let ResponseScript { plutus_type,.. } = response.json::().await.unwrap(); + // } + // _ => Err(anyhow!("No script found")), + // } + // } +} + +pub fn plutus_data_from_inline(inline_datum: &str) -> Result { + Ok(decode(&hex::decode(inline_datum)?)?) +} + +impl Cardano for Blockfrost { + fn network_id(&self) -> Network { + self.network + } + + async fn build_parameters(&self) -> BuildParams { + let params = self + .api + .epochs_latest_parameters() + .await + .expect("failed to fetch protocol parameters"); + + let pp = ProtocolParameters { + collateral_percent: (params + .collateral_percent + .expect("protocol parameters are missing collateral percent") + as f64) + / 1e2, + // NOTE: Blockfrost returns cost models out of order. They must be ordered by their + // "ParamName" according to how Plutus defines it, but they are usually found ordered + // by ascending keys, unfortunately. Given that they are unlikely to change anytime + // soon, I am going to bundle them as-is. + cost_model_v3: vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, + 769, 4, 2, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 1000, 42921, + 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, + 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, + 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, + 32, 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 955506, 213312, 0, 2, + 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, + 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, 16000, 100, 16000, 100, + 962335, 18, 2780678, 6, 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, + 18, 52948122, 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, + 36, 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, 72, + 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, 43623, 251, + 0, 1, + ], + drep_deposit: 500_000_000, // NOTE: Missing from Blockfrost + fee_constant: params.min_fee_b as u64, + fee_coefficient: params.min_fee_a as u64, + min_utxo_deposit_coefficient: params + .coins_per_utxo_size + .expect("protocol parameters are missing min utxo deposit coefficient") + .parse() + .unwrap(), + price_mem: params + .price_mem + .expect("protocol parameters are missing price mem") as f64, + price_steps: params + .price_step + .expect("protocol parameters are missing price step") + as f64, + }; + (&pp).into() + } + + async fn resolve_many(&self, inputs: Vec) -> Vec { + let mut resolved = vec![]; + for i in inputs { + if let Ok(r) = self.resolve(i).await { + resolved.push(r) + } + } + resolved + } + + async fn resolve(&self, input: TransactionInput) -> Result { + let utxo = self + .api + .transactions_utxos(hex::encode(input.transaction_id).as_str()) + .await?; + + if let Some(output) = utxo + .outputs + .into_iter() + .filter(|o| !o.collateral) + .nth(input.index as usize) + { + assert_eq!( + output.output_index, input.index as i32, + "somehow resolved the wrong ouput", + ); + let datum_option = self + .resolve_datum_option(&output.data_hash, &output.inline_datum) + .await + .expect("Something went wrong"); + // let script_ref = self.resolve_script(&output.reference_script_hash).await.expect("Something went wrong"); + + // FIXME!!!! + let script_ref = None; + + Ok(ResolvedInput { + input: input.clone(), + output: TransactionOutput::PostAlonzo(PostAlonzoTransactionOutput { + address: from_bech32(&output.address).into(), + value: from_tx_content_output_amounts(&output.amount[..]), + datum_option, + script_ref, + }), + }) + } else { + Err(anyhow!("No output found")) + } + } + + async fn transaction_by_hash(&self, tx_hash: &str) -> Result { + // NOTE: Not part of the Rust SDK somehow... + let response = self + .client + .get(&format!("{}/txs/{}/cbor", self.base_url, tx_hash)) + .header("Accept", "application/json") + .header("project_id", self.project_id.as_str()) + .send() + .await + .unwrap(); + + match response.status() { + reqwest::StatusCode::OK => { + let ResponseCbor { cbor } = response.json::().await.unwrap(); + let bytes = hex::decode(cbor).unwrap(); + let tx = cbor::decode(&bytes).unwrap(); + Ok(tx) + } + _ => Err(anyhow!("No tx found")), + } + } + async fn health(&self) -> Result { + match self.api.health().await { + Ok(x) => Ok(format!("{:?}", x)), + Err(y) => Err(y.to_string()), + } + } + + async fn utxos_at( + &self, + payment_credential: &ShelleyPaymentPart, + ) -> Result> { + let addr = ShelleyAddress::new( + self.network_id(), + payment_credential.clone(), + ShelleyDelegationPart::Null, + ); + let response = self + .api + .addresses_utxos(&addr.to_bech32()?, Pagination::all()) + .await?; + response + .iter() + .map(|o| { + // FIXME: This should pull the datum and script reference + let datum_option = None; + let script_ref = None; + Ok(ResolvedInput { + input: TransactionInput { + transaction_id: v2a(hex::decode(&o.tx_hash)?)?.into(), + index: o.tx_index as u64, + }, + output: TransactionOutput::PostAlonzo(PostAlonzoTransactionOutput { + address: from_bech32(&o.address).into(), + value: from_tx_content_output_amounts(&o.amount[..]), + datum_option, + script_ref, + }), + }) + }) + .collect() + } + + async fn submit(&self, tx: Vec) -> Result { + let tx_hash = self.api.transactions_submit(tx).await?; + Ok(tx_hash) + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +struct ResponseCbor { + cbor: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +struct ResponseScript { + script_hash: String, + #[serde(rename = "type")] + plutus_type: String, + serialised_size: u64, +} + +fn from_bech32(bech32: &str) -> Vec { + bech32::decode(bech32).unwrap().1 +} + +fn from_tx_content_output_amounts(xs: &[TxContentOutputAmountInner]) -> Value { + let mut lovelaces = 0; + let mut assets = BTreeMap::new(); + + for asset in xs { + let quantity: u64 = asset.quantity.parse().unwrap(); + if asset.unit == UNIT_LOVELACE { + lovelaces += quantity; + } else { + let policy_id: PolicyId = asset.unit[0..56].parse().unwrap(); + let asset_name: AssetName = hex::decode(&asset.unit[56..]).unwrap().into(); + assets + .entry(policy_id) + .and_modify(|m: &mut BTreeMap| { + m.entry(asset_name.clone()) + .and_modify(|q| *q += quantity) + .or_insert(quantity); + }) + .or_insert_with(|| BTreeMap::from([(asset_name, quantity)])); + } + } + + if assets.is_empty() { + Value::Coin(lovelaces) + } else { + Value::Multiasset( + lovelaces, + NonEmptyKeyValuePairs::Def( + assets + .into_iter() + .map(|(policy_id, policies)| { + ( + policy_id, + NonEmptyKeyValuePairs::Def( + policies + .into_iter() + .map(|(asset_name, quantity)| { + (asset_name, quantity.try_into().unwrap()) + }) + .collect::>(), + ), + ) + }) + .collect::>(), + ), + ) + } +} diff --git a/src/cardano/cardano.rs b/src/cardano/cardano.rs new file mode 100644 index 0000000..cb44be2 --- /dev/null +++ b/src/cardano/cardano.rs @@ -0,0 +1,31 @@ +use anyhow::Result; +use pallas_addresses::{Network, ShelleyPaymentPart}; +use pallas_primitives::conway::Tx; +use uplc::{tx::ResolvedInput, TransactionInput}; + +use crate::tx::plutus::BuildParams; + +pub trait Cardano { + fn network_id(&self) -> Network; + fn build_parameters(&self) -> impl std::future::Future + Send; + fn resolve_many( + &self, + inputs: Vec, + ) -> impl std::future::Future> + Send; + fn resolve( + &self, + input: TransactionInput, + ) -> impl std::future::Future> + Send; + fn transaction_by_hash( + &self, + tx_hash: &str, + ) -> impl std::future::Future> + Send; + /// This should return all utxos available to be spent, + /// ie regardelss of the staking part. + fn utxos_at( + &self, + payment_credential: &ShelleyPaymentPart, + ) -> impl std::future::Future>> + Send; + fn health(&self) -> impl std::future::Future> + Send; + fn submit(&self, tx: Vec) -> impl std::future::Future> + Send; +} diff --git a/src/cmd.rs b/src/cmd.rs new file mode 100644 index 0000000..5525b4a --- /dev/null +++ b/src/cmd.rs @@ -0,0 +1,28 @@ +use clap::Subcommand; + +mod cardano; +mod data; +mod tx; +mod wallet; + +#[derive(Subcommand)] +pub enum Cmd { + /// Txs + #[command(subcommand)] + Tx(tx::Cmd), + #[command(subcommand)] + Data(data::Cmd), + #[command(subcommand)] + Cardano(cardano::Cmd), + #[command(subcommand)] + Wallet(wallet::Cmd), +} + +pub fn handle(cmd: Cmd) { + match cmd { + Cmd::Tx(inner) => tx::handle(inner), + Cmd::Data(inner) => data::handle(inner), + Cmd::Cardano(inner) => cardano::handle(inner), + Cmd::Wallet(inner) => wallet::handle(inner), + }; +} diff --git a/src/cmd/cardano.rs b/src/cmd/cardano.rs new file mode 100644 index 0000000..bcbece8 --- /dev/null +++ b/src/cmd/cardano.rs @@ -0,0 +1,25 @@ +use clap::Subcommand; +use tokio::runtime::Runtime; + +use crate::cardano::{cardano::Cardano, from_env}; +use crate::env::get_env; + +#[derive(Subcommand)] +/// Cardano api +pub enum Cmd { + /// Health + Health, +} + +pub fn handle(cmd: Cmd) { + match cmd { + Cmd::Health => { + let env = get_env(); + let conn = from_env(&env); + let rt = Runtime::new().expect("Failed to create Tokio runtime"); + rt.block_on(async { + println!("{:?}", conn.health().await); + }) + } + }; +} diff --git a/src/cmd/data.rs b/src/cmd/data.rs new file mode 100644 index 0000000..9e19447 --- /dev/null +++ b/src/cmd/data.rs @@ -0,0 +1,47 @@ +use clap::Subcommand; + +use crate::data::{ + base::{Amount, Hash28, Tag, TimeDelta, VKey}, + constants::Constants, + datum::Datum, + plutus::to_cbor, + stage::Stage, +}; + +#[derive(Subcommand)] +/// Data +pub enum Cmd { + /// Make an example (serialised) datum + Datum, + /// other + Other, +} + +pub fn handle(cmd: Cmd) { + match cmd { + Cmd::Datum => handle_datum(), + _ => println!("Not yet implemented"), + }; +} + +fn handle_datum() { + let own_hash = Hash28([0; 28]); + let tag = Tag([1; 8].to_vec()); + let add_vkey = VKey([2; 32]); + let sub_vkey = VKey([3; 32]); + let close_period = TimeDelta(86_400_000); + let constants = Constants { + tag, + add_vkey, + sub_vkey, + close_period, + }; + let subbed = Amount(0x4444444444); + let stage = Stage::Opened(subbed); + let datum = Datum { + own_hash, + constants, + stage, + }; + println!("{}", hex::encode(to_cbor(&datum).unwrap())) +} diff --git a/src/cmd/tx.rs b/src/cmd/tx.rs new file mode 100644 index 0000000..40815f3 --- /dev/null +++ b/src/cmd/tx.rs @@ -0,0 +1,28 @@ +use clap::Subcommand; + +mod open; +mod send; + +#[derive(Subcommand)] +/// Txs +pub enum Cmd { + /// Send + Send(send::Params), + /// Open + Open(open::Params), + /// Add + Add, + /// Sub + Sub, + /// Close + Close, +} + +pub fn handle(cmd: Cmd) { + match cmd { + Cmd::Open(params) => open::handle(params), + Cmd::Send(params) => send::handle(params), + Cmd::Close => println!("Do Close"), + _ => todo!(), + }; +} diff --git a/src/cmd/tx/open.rs b/src/cmd/tx/open.rs new file mode 100644 index 0000000..6bf44b1 --- /dev/null +++ b/src/cmd/tx/open.rs @@ -0,0 +1,21 @@ +use clap::Args; + +#[derive(Debug, Args)] +pub struct Params { + /// The channel tag + #[arg(long, default_value = "hello")] + tag: String, + /// The adaptors vkey + #[arg(long, default_value = "")] + adaptor: String, + /// Amount (in ada) to put in the channel + #[arg(long, default_value = "10")] + amount: usize, + /// Minimum time (in milliseconds) from `close` to `elapse` + #[arg(long, default_value = "86_400_000")] + close_period: usize, +} + +pub fn handle(params: Params) { + println!("{:?}", params); +} diff --git a/src/cmd/tx/send.rs b/src/cmd/tx/send.rs new file mode 100644 index 0000000..82db60e --- /dev/null +++ b/src/cmd/tx/send.rs @@ -0,0 +1,40 @@ +use clap::Args; +use pallas_addresses::Address; +use tokio::runtime::Runtime; + +use crate::{ + cardano::cardano::Cardano, + env::get_env, + tx::{ + context::TxContext, + from_env, + send::{self, send}, + }, +}; + +#[derive(Debug, Args)] +pub struct Params { + /// `:` It can be used multiple times + #[arg(long)] + to: Vec, +} + +pub fn handle(params: Params) { + let outputs = params + .to + .iter() + .map(|x| parse_to(x)) + .collect::>(); + let env = get_env(); + let tx_ctx = from_env(&env); + let rt = Runtime::new().expect("Failed to create Tokio runtime"); + let tx = rt.block_on(async { send(tx_ctx, vec![]).await }); + println!("{:?}", tx); +} + +pub fn parse_to(to: &str) -> (Address, u64) { + let (a, b) = to.split_once(":").expect("Expect `:`"); + let addr = Address::from_bech32(a).expect("Cannot parse address"); + let amount = b.parse::().expect("Cannot parse amount"); + (addr, amount) +} diff --git a/src/cmd/wallet.rs b/src/cmd/wallet.rs new file mode 100644 index 0000000..3d95419 --- /dev/null +++ b/src/cmd/wallet.rs @@ -0,0 +1,59 @@ +use clap::Subcommand; +use pallas_addresses::{ + Address, Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart, +}; +use tokio::runtime::Runtime; + +use crate::cardano; +use crate::cardano::cardano::Cardano; +use crate::env::get_env; +use crate::wallet::{from_env, generate, Wallet}; + +#[derive(Subcommand)] +/// Wallet Api +pub enum Cmd { + /// Gen new skey + Gen, + /// Show + Show, + /// Utxos at address. Requires `Cardano` connection. + Utxos, +} + +pub fn handle(cmd: Cmd) { + match cmd { + Cmd::Gen => { + println!("KONDUIT_WALLET_KEY={}", hex::encode(generate())); + } + Cmd::Show => { + let env = get_env(); + let w = from_env(&env); + let payment_cred = ShelleyPaymentPart::Key(w.key_hash()); + let stake_cred = ShelleyDelegationPart::Null; + let addr_main = + ShelleyAddress::new(Network::Mainnet, payment_cred.clone(), stake_cred.clone()); + let addr_test = + ShelleyAddress::new(Network::Testnet, payment_cred.clone(), stake_cred.clone()); + println!("VKEY={}", hex::encode(w.vkey())); + println!("PAYMENT_CRED={:?}", payment_cred.to_bech32()); + println!("ADDRESS_MAINNET={:?}", addr_main.to_bech32()); + println!("ADDRESS_TESTNET={:?}", addr_test.to_bech32()); + } + Cmd::Utxos => { + let env = get_env(); + let w = from_env(&env); + let payment_cred = w.payment_credential(); + let stake_cred = ShelleyDelegationPart::Null; + let conn = cardano::from_env(&env); + let rt = Runtime::new().expect("Failed to create Tokio runtime"); + rt.block_on(async { + let addr = ShelleyAddress::new( + conn.network_id(), + payment_cred.clone(), + stake_cred.clone(), + ); + println!("{:?}", conn.utxos_at(&w.payment_credential()).await); + }) + } + }; +} diff --git a/src/data.rs b/src/data.rs new file mode 100644 index 0000000..8e27944 --- /dev/null +++ b/src/data.rs @@ -0,0 +1,12 @@ +pub mod base; +pub mod cheque_body; +pub mod constants; +pub mod datum; +pub mod mix; +pub mod pend_cheque; +pub mod plutus; +pub mod redeemer; +pub mod squash; +pub mod stage; +pub mod step; +pub mod unlocked; diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000..7899c19 --- /dev/null +++ b/src/env.rs @@ -0,0 +1,16 @@ +use std::collections::HashMap; + +/// Used for variables that are roughly constant per "instance" +/// These include signing keys and cardano connection + +const PREFIX: &str = "KONDUIT_"; + +pub fn get_env() -> HashMap { + let mut env = HashMap::new(); + for (key, value) in std::env::vars() { + if let Some(key) = key.strip_prefix(PREFIX) { + env.insert(key.to_lowercase(), value); + }; + } + env +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9e569eb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +pub mod cardano; +pub mod cmd; +pub mod data; +pub mod env; +pub mod pallas_extra; +pub mod tx; +pub mod utils; +pub mod wallet; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..addeedf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,18 @@ +use clap::Parser; + +use kon_gen::cmd; + +#[derive(Parser)] +#[command(arg_required_else_help(true), version, about)] +struct Args { + #[command(subcommand)] + cmd: Option, +} + +fn main() { + let args = Args::parse(); + match args.cmd { + Some(cmd) => cmd::handle(cmd), + None => println!("See help"), + }; +} diff --git a/src/pallas_extra.rs b/src/pallas_extra.rs new file mode 100644 index 0000000..c047b57 --- /dev/null +++ b/src/pallas_extra.rs @@ -0,0 +1,397 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// SOURCE : github.com/cardanoSolutions/zhuli + +use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart}; +use pallas_codec::{ + minicbor as cbor, + utils::{NonEmptyKeyValuePairs, NonEmptySet, Set}, +}; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_primitives::{ + conway::{ + AssetName, Constr, ExUnits, Language, Multiasset, NetworkId, PlutusData, + PostAlonzoTransactionOutput, PseudoTransactionOutput, RedeemerTag, RedeemersKey, + RedeemersValue, TransactionBody, TransactionInput, TransactionOutput, Tx, Value, + WitnessSet, + }, + MaybeIndefArray, +}; +use std::{cmp::Ordering, str::FromStr}; +use uplc::tx::{eval_phase_two, ResolvedInput, SlotConfig}; + +#[derive(Debug)] +pub struct BuildParams { + pub fee_constant: u64, + pub fee_coefficient: u64, + pub price_mem: f64, + pub price_steps: f64, +} + +pub struct OutputReference(pub TransactionInput); + +impl FromStr for OutputReference { + type Err = String; + fn from_str(s: &str) -> Result { + match &s.split('#').collect::>()[..] { + [tx_id_str, ix_str] => { + let transaction_id: Hash<32> = tx_id_str + .parse() + .map_err(|e| format!("failed to decode transaction id from hex: {e:?}"))?; + let index: u64 = ix_str + .parse() + .map_err(|e| format!("failed to decode output index: {e:?}"))?; + Ok(OutputReference(TransactionInput { + transaction_id, + index, + })) + } + _ => Err("malformed output reference: expected a hex-encode string and an index separated by '#'".to_string()), + } + } +} + +pub struct Redeemer {} + +impl Redeemer { + pub fn mint(index: u32, data: PlutusData, ex_units: ExUnits) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Mint, + index, + }, + RedeemersValue { data, ex_units }, + ) + } + + pub fn spend( + (inputs, target): (&[TransactionInput], &TransactionInput), + data: PlutusData, + ex_units: ExUnits, + ) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Spend, + index: inputs + .iter() + .enumerate() + .find(|(_, i)| *i == target) + .unwrap() + .0 as u32, + }, + RedeemersValue { data, ex_units }, + ) + } + + pub fn publish( + index: u32, + data: PlutusData, + ex_units: ExUnits, + ) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Cert, + index, + }, + RedeemersValue { data, ex_units }, + ) + } + + pub fn vote(index: u32, data: PlutusData, ex_units: ExUnits) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Vote, + index, + }, + RedeemersValue { data, ex_units }, + ) + } +} + +pub fn void() -> PlutusData { + PlutusData::Constr(Constr { + tag: 121, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }) +} + +pub fn from_network(network: Network) -> NetworkId { + match network { + Network::Mainnet => NetworkId::Mainnet, + _ => NetworkId::Testnet, + } +} + +pub fn non_empty_set(set: Vec) -> Option> +where + T: std::fmt::Debug, +{ + if set.is_empty() { + None + } else { + Some(NonEmptySet::try_from(set).unwrap()) + } +} + +pub fn non_empty_pairs(pairs: Vec<(K, V)>) -> Option> +where + V: Clone, + K: Clone, +{ + if pairs.is_empty() { + None + } else { + Some(NonEmptyKeyValuePairs::Def(pairs)) + } +} + +pub fn into_outputs(outputs: Vec) -> Vec { + outputs + .into_iter() + .map(PseudoTransactionOutput::PostAlonzo) + .collect() +} + +pub fn singleton_assets( + validator_hash: Hash<28>, + assets: &[(AssetName, T)], +) -> Multiasset { + NonEmptyKeyValuePairs::Def(vec![( + validator_hash, + NonEmptyKeyValuePairs::Def(assets.to_vec()), + )]) +} + +pub fn from_validator(validator: &[u8], network_id: Network) -> (Hash<28>, ShelleyAddress) { + let validator_hash = Hasher::<224>::hash_tagged(validator, 3); + let validator_address = ShelleyAddress::new( + network_id, + ShelleyPaymentPart::script_hash(validator_hash), + ShelleyDelegationPart::script_hash(validator_hash), + ); + + (validator_hash, validator_address) +} + +pub fn value_subtract_lovelace(value: Value, lovelace: u64) -> Option { + match value { + Value::Coin(total) if total > lovelace => Some(Value::Coin(total - lovelace)), + Value::Multiasset(total, assets) if total > lovelace => { + Some(Value::Multiasset(total - lovelace, assets)) + } + _ => None, + } +} + +pub fn value_add_lovelace(value: Value, lovelace: u64) -> Value { + match value { + Value::Coin(total) => Value::Coin(total + lovelace), + Value::Multiasset(total, assets) => Value::Multiasset(total + lovelace, assets), + } +} + +pub fn lovelace_of(value: &Value) -> u64 { + match value { + Value::Coin(lovelace) | Value::Multiasset(lovelace, _) => *lovelace, + } +} + +pub fn new_min_value_output(per_byte: u64, build: F) -> PostAlonzoTransactionOutput +where + F: Fn(u64) -> PostAlonzoTransactionOutput, +{ + let value = build(1); + let mut buffer: Vec = Vec::new(); + cbor::encode(&value, &mut buffer).unwrap(); + // NOTE: 160 overhead as per the spec + 4 bytes for actual final lovelace value. + // Technically, the final value could need 8 more additional bytes if the resulting + // value was larger than 4_294_967_295 lovelaces, which would realistically never be + // the case. + build((buffer.len() as u64 + 164) * per_byte) +} + +pub fn total_execution_cost(params: &BuildParams, redeemers: &[ExUnits]) -> u64 { + redeemers.iter().fold(0, |acc, ex_units| { + acc + ((params.price_mem * ex_units.mem as f64).ceil() as u64) + + ((params.price_steps * ex_units.steps as f64).ceil() as u64) + }) +} + +pub fn script_integrity_hash( + redeemers: Option<&NonEmptyKeyValuePairs>, + datums: Option<&NonEmptyKeyValuePairs, PlutusData>>, + language_views: &[(Language, &[i64])], +) -> Option> { + if redeemers.is_none() && language_views.is_empty() && datums.is_none() { + return None; + } + + let mut preimage: Vec = Vec::new(); + if let Some(redeemers) = redeemers { + cbor::encode(redeemers, &mut preimage).unwrap(); + } + + if let Some(datums) = datums { + cbor::encode(datums, &mut preimage).unwrap(); + } + + // NOTE: This doesn't work for PlutusV1, but I don't care. + if !language_views.is_empty() { + let mut views = language_views.to_vec(); + // TODO: Derive an Ord instance in Pallas. + views.sort_by(|(a, _), (b, _)| match (a, b) { + (Language::PlutusV3, Language::PlutusV3) => Ordering::Equal, + (Language::PlutusV3, _) => Ordering::Greater, + (_, Language::PlutusV3) => Ordering::Less, + + (Language::PlutusV2, Language::PlutusV2) => Ordering::Equal, + (Language::PlutusV2, _) => Ordering::Greater, + (_, Language::PlutusV2) => Ordering::Less, + + (Language::PlutusV1, Language::PlutusV1) => Ordering::Equal, + }); + cbor::encode(NonEmptyKeyValuePairs::Def(views), &mut preimage).unwrap() + } + + Some(Hasher::<256>::hash(&preimage)) +} + +pub fn default_transaction_body() -> TransactionBody { + TransactionBody { + auxiliary_data_hash: None, + certificates: None, + collateral: None, + collateral_return: None, + donation: None, + fee: 0, + inputs: Set::from(vec![]), + mint: None, + network_id: None, + outputs: vec![], + proposal_procedures: None, + reference_inputs: None, + required_signers: None, + script_data_hash: None, + total_collateral: None, + treasury_value: None, + ttl: None, + validity_interval_start: None, + voting_procedures: None, + withdrawals: None, + } +} + +pub fn default_witness_set() -> WitnessSet { + WitnessSet { + bootstrap_witness: None, + native_script: None, + plutus_data: None, + plutus_v1_script: None, + plutus_v2_script: None, + plutus_v3_script: None, + redeemer: None, + vkeywitness: None, + } +} + +// Build a transaction by repeatedly executing some building logic with different fee and execution +// units settings. Stops when a fixed point is reached. The final transaction has corresponding +// fees and execution units. +pub fn build_transaction(params: &BuildParams, resolved_inputs: &[ResolvedInput], with: F) -> Tx +where + F: Fn(u64, &[ExUnits]) -> Tx, +{ + let empty_ex_units = || { + vec![ + ExUnits { mem: 0, steps: 0 }, + ExUnits { mem: 0, steps: 0 }, + ExUnits { mem: 0, steps: 0 }, + ExUnits { mem: 0, steps: 0 }, + ] + }; + + let mut fee = 0; + let mut ex_units = empty_ex_units(); + let mut tx; + let mut attempts = 0; + loop { + tx = with(fee, &ex_units[..]); + + // Convert to minted_tx... + let mut serialized_tx = Vec::new(); + cbor::encode(&tx, &mut serialized_tx).unwrap(); + + let mut calculated_ex_units = if resolved_inputs.is_empty() { + empty_ex_units() + } else { + // Compute execution units + let minted_tx = cbor::decode(&serialized_tx).unwrap(); + eval_phase_two( + &minted_tx, + resolved_inputs, + None, + None, + &SlotConfig::default(), + false, + |_| (), + ) + .expect("script evaluation failed") + .into_iter() + .map(|r| r.0.ex_units) + .collect::>() + }; + + calculated_ex_units.extend(empty_ex_units()); + + attempts += 1; + + let estimated_fee = { + // NOTE: This is a best effort to estimate the number of signatories since signatures + // will add an overhead to the fee. Yet, if inputs are locked by native scripts each + // requiring multiple signatories, this will unfortunately fall short. + // + // For similar reasons, it will also over-estimate fees by a small margin for every + // script-locked inputs that do not require signatories. + // + // This is however *acceptable* in our context. + let num_signatories = tx.transaction_body.inputs.len() + + tx.transaction_body + .required_signers + .as_ref() + .map(|xs| xs.len()) + .unwrap_or(0); + + params.fee_constant + + params.fee_coefficient + * (5 + ex_units.len() * 16 + num_signatories * 102 + serialized_tx.len()) as u64 + + total_execution_cost(params, &ex_units) + }; + + // Check if we've reached a fixed point, or start over. + if fee >= estimated_fee + && calculated_ex_units + .iter() + .zip(ex_units) + .all(|(l, r)| l.eq(&r)) + { + break; + } else if attempts >= 3 { + panic!("failed to build transaction: did not converge after three attempts."); + } else { + ex_units = calculated_ex_units; + fee = estimated_fee; + } + } + tx +} + +pub fn expect_post_alonzo(output: &TransactionOutput) -> &PostAlonzoTransactionOutput { + if let TransactionOutput::PostAlonzo(ref o) = output { + o + } else { + panic!("expected PostAlonzo output but got a legacy one.") + } +} diff --git a/src/tx.rs b/src/tx.rs new file mode 100644 index 0000000..f043650 --- /dev/null +++ b/src/tx.rs @@ -0,0 +1,18 @@ +use std::collections::HashMap; + +use crate::cardano; +use crate::cardano::cardano::Cardano; +use crate::wallet; + +pub mod context; +mod open; +pub mod plutus; +pub mod send; +mod torso; + +pub fn from_env(env: &HashMap) -> context::TxContext { + context::TxContext { + cardano: cardano::from_env(env), + wallet: wallet::from_env(env), + } +} diff --git a/src/tx/context.rs b/src/tx/context.rs new file mode 100644 index 0000000..95add0b --- /dev/null +++ b/src/tx/context.rs @@ -0,0 +1,31 @@ +use crate::{cardano::cardano::Cardano, wallet::Wallet}; +use anyhow::Result; +use pallas_addresses::{ShelleyAddress, ShelleyDelegationPart}; +use uplc::tx::ResolvedInput; + +use super::plutus::BuildParams; + +pub struct TxContext { + pub cardano: CardanoT, + pub wallet: Wallet, +} + +impl TxContext { + pub async fn build_parameters(&self) -> BuildParams { + self.cardano.build_parameters().await + } + + pub async fn available_utxos(&self) -> Result> { + self.cardano + .utxos_at(&self.wallet.payment_credential()) + .await + } + + pub fn wallet_address(&self) -> ShelleyAddress { + ShelleyAddress::new( + self.cardano.network_id(), + self.wallet.payment_credential(), + ShelleyDelegationPart::Null, + ) + } +} diff --git a/src/tx/open.rs b/src/tx/open.rs new file mode 100644 index 0000000..b248d21 --- /dev/null +++ b/src/tx/open.rs @@ -0,0 +1,105 @@ +// pub struct Instance { +// cardano: Cardano, +// contract_reference : OutputReference, +// wallet_key: SigningKey, +// } + +// pub async fn open( +// instance +// network: Cardano, +// delegates: Vec>, +// choice: Vote, +// anchor: Option, +// proposal_id: GovActionId, +// OutputReference(contract): OutputReference, +// OutputReference(collateral): OutputReference, +// ) -> Tx { +// let (validator, validator_hash, _) = +// recover_validator(&network, &contract.transaction_id).await; +// +// let params = network.protocol_parameters().await; +// +// let resolved_inputs = network.resolve_many(&[&collateral, &contract]).await; +// let fuel_output = expect_post_alonzo(&resolved_inputs[0].output); +// let contract_output = expect_post_alonzo(&resolved_inputs[1].output); +// +// let (rules, _) = recover_rules(&network, &validator_hash, &contract_output.value).await; +// +// build_transaction( +// &BuildParams::from(¶ms), +// &resolved_inputs[..], +// |fee, ex_units| { +// let mut redeemers = vec![]; +// +// let inputs = vec![collateral.clone()]; +// +// let reference_inputs = vec![contract.clone()]; +// +// let outputs = vec![ +// // Change +// PostAlonzoTransactionOutput { +// address: fuel_output.address.clone(), +// value: value_subtract_lovelace(fuel_output.value.clone(), fee) +// .expect("not enough fuel"), +// datum_option: None, +// script_ref: None, +// }, +// ]; +// +// let total_collateral = (fee as f64 * params.collateral_percent).ceil() as u64; +// +// let collateral_return = PostAlonzoTransactionOutput { +// address: fuel_output.address.clone(), +// value: value_subtract_lovelace(fuel_output.value.clone(), total_collateral) +// .expect("not enough fuel"), +// datum_option: None, +// script_ref: None, +// }; +// +// let votes = vec![( +// Voter::DRepScript(validator_hash), +// NonEmptyKeyValuePairs::Def(vec![( +// proposal_id.clone(), +// VotingProcedure { +// vote: choice.clone(), +// anchor: anchor.clone().map(Nullable::Some).unwrap_or(Nullable::Null), +// }, +// )]), +// )]; +// redeemers.push(Redeemer::vote(0, rules.clone(), ex_units[0])); +// +// // ----- Put it all together +// let redeemers = non_empty_pairs(redeemers).unwrap(); +// Tx { +// transaction_body: TransactionBody { +// inputs: Set::from(inputs), +// reference_inputs: non_empty_set(reference_inputs), +// network_id: Some(from_network(network.network_id())), +// outputs: into_outputs(outputs), +// voting_procedures: non_empty_pairs(votes), +// fee, +// collateral: non_empty_set(vec![fuel.clone()]), +// collateral_return: Some(PseudoTransactionOutput::PostAlonzo(collateral_return)), +// total_collateral: Some(total_collateral), +// required_signers: non_empty_set(delegates.clone()), +// script_data_hash: Some( +// script_integrity_hash( +// Some(&redeemers), +// None, +// &[(Language::PlutusV3, ¶ms.cost_model_v3[..])], +// ) +// .unwrap(), +// ), +// ..default_transaction_body() +// }, +// transaction_witness_set: WitnessSet { +// redeemer: Some(redeemers.into()), +// plutus_v3_script: non_empty_set(vec![PlutusScript::<3>(validator.clone())]), +// ..default_witness_set() +// }, +// success: true, +// auxiliary_data: Nullable::Null, +// } +// }, +// ) +// } diff --git a/src/tx/plutus.rs b/src/tx/plutus.rs new file mode 100644 index 0000000..9e73b17 --- /dev/null +++ b/src/tx/plutus.rs @@ -0,0 +1,399 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// SOURCE : github.com/cardanoSolutions/zhuli + +use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart}; +use pallas_codec::{ + minicbor as cbor, + utils::{NonEmptyKeyValuePairs, NonEmptySet, Set}, +}; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_primitives::{ + conway::{ + AssetName, Constr, ExUnits, Language, Multiasset, NetworkId, PlutusData, + PostAlonzoTransactionOutput, PseudoTransactionOutput, RedeemerTag, RedeemersKey, + RedeemersValue, TransactionBody, TransactionInput, TransactionOutput, Tx, Value, + WitnessSet, + }, + MaybeIndefArray, +}; +use std::{cmp::Ordering, str::FromStr}; +use uplc::tx::{eval_phase_two, ResolvedInput, SlotConfig}; + +#[derive(Debug)] +pub struct BuildParams { + pub fee_constant: u64, + pub fee_coefficient: u64, + pub price_mem: f64, + pub price_steps: f64, +} + +pub struct OutputReference(pub TransactionInput); + +impl FromStr for OutputReference { + type Err = String; + fn from_str(s: &str) -> Result { + match &s.split('#').collect::>()[..] { + [tx_id_str, ix_str] => { + let transaction_id: Hash<32> = tx_id_str + .parse() + .map_err(|e| format!("failed to decode transaction id from hex: {e:?}"))?; + let index: u64 = ix_str + .parse() + .map_err(|e| format!("failed to decode output index: {e:?}"))?; + Ok(OutputReference(TransactionInput { + transaction_id, + index, + })) + } + _ => Err("malformed output reference: expected a hex-encode string and an index separated by '#'".to_string()), + } + } +} + +pub struct Redeemer {} + +impl Redeemer { + pub fn mint(index: u32, data: PlutusData, ex_units: ExUnits) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Mint, + index, + }, + RedeemersValue { data, ex_units }, + ) + } + + pub fn spend( + (inputs, target): (&[TransactionInput], &TransactionInput), + data: PlutusData, + ex_units: ExUnits, + ) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Spend, + index: inputs + .iter() + .enumerate() + .find(|(_, i)| *i == target) + .unwrap() + .0 as u32, + }, + RedeemersValue { data, ex_units }, + ) + } + + pub fn publish( + index: u32, + data: PlutusData, + ex_units: ExUnits, + ) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Cert, + index, + }, + RedeemersValue { data, ex_units }, + ) + } + + pub fn vote(index: u32, data: PlutusData, ex_units: ExUnits) -> (RedeemersKey, RedeemersValue) { + ( + RedeemersKey { + tag: RedeemerTag::Vote, + index, + }, + RedeemersValue { data, ex_units }, + ) + } +} + +pub fn void() -> PlutusData { + PlutusData::Constr(Constr { + tag: 121, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![]), + }) +} + +pub fn from_network(network: Network) -> NetworkId { + match network { + Network::Mainnet => NetworkId::Mainnet, + _ => NetworkId::Testnet, + } +} + +pub fn non_empty_set(set: Vec) -> Option> +where + T: std::fmt::Debug, +{ + if set.is_empty() { + None + } else { + Some(NonEmptySet::try_from(set).unwrap()) + } +} + +pub fn non_empty_pairs(pairs: Vec<(K, V)>) -> Option> +where + V: Clone, + K: Clone, +{ + if pairs.is_empty() { + None + } else { + Some(NonEmptyKeyValuePairs::Def(pairs)) + } +} + +pub fn into_outputs(outputs: Vec) -> Vec { + outputs + .into_iter() + .map(PseudoTransactionOutput::PostAlonzo) + .collect() +} + +pub fn singleton_assets( + validator_hash: Hash<28>, + assets: &[(AssetName, T)], +) -> Multiasset { + NonEmptyKeyValuePairs::Def(vec![( + validator_hash, + NonEmptyKeyValuePairs::Def(assets.to_vec()), + )]) +} + +pub fn from_validator(validator: &[u8], network_id: Network) -> (Hash<28>, ShelleyAddress) { + let validator_hash = Hasher::<224>::hash_tagged(validator, 3); + let validator_address = ShelleyAddress::new( + network_id, + ShelleyPaymentPart::script_hash(validator_hash), + ShelleyDelegationPart::script_hash(validator_hash), + ); + + (validator_hash, validator_address) +} + +pub fn value_subtract_lovelace(value: Value, lovelace: u64) -> Option { + match value { + Value::Coin(total) if total > lovelace => Some(Value::Coin(total - lovelace)), + Value::Multiasset(total, assets) if total > lovelace => { + Some(Value::Multiasset(total - lovelace, assets)) + } + _ => None, + } +} + +pub fn value_add_lovelace(value: Value, lovelace: u64) -> Value { + match value { + Value::Coin(total) => Value::Coin(total + lovelace), + Value::Multiasset(total, assets) => Value::Multiasset(total + lovelace, assets), + } +} + +pub fn lovelace_of(value: &Value) -> u64 { + match value { + Value::Coin(lovelace) | Value::Multiasset(lovelace, _) => *lovelace, + } +} + +pub fn new_min_value_output(per_byte: u64, build: F) -> PostAlonzoTransactionOutput +where + F: Fn(u64) -> PostAlonzoTransactionOutput, +{ + let value = build(1); + let mut buffer: Vec = Vec::new(); + cbor::encode(&value, &mut buffer).unwrap(); + // NOTE: 160 overhead as per the spec + 4 bytes for actual final lovelace value. + // Technically, the final value could need 8 more additional bytes if the resulting + // value was larger than 4_294_967_295 lovelaces, which would realistically never be + // the case. + build((buffer.len() as u64 + 164) * per_byte) +} + +pub fn total_execution_cost(params: &BuildParams, redeemers: &[ExUnits]) -> u64 { + redeemers.iter().fold(0, |acc, ex_units| { + acc + ((params.price_mem * ex_units.mem as f64).ceil() as u64) + + ((params.price_steps * ex_units.steps as f64).ceil() as u64) + }) +} + +pub fn script_integrity_hash( + redeemers: Option<&NonEmptyKeyValuePairs>, + datums: Option<&NonEmptyKeyValuePairs, PlutusData>>, + language_views: &[(Language, &[i64])], +) -> Option> { + if redeemers.is_none() && language_views.is_empty() && datums.is_none() { + return None; + } + + let mut preimage: Vec = Vec::new(); + if let Some(redeemers) = redeemers { + cbor::encode(redeemers, &mut preimage).unwrap(); + } + + if let Some(datums) = datums { + cbor::encode(datums, &mut preimage).unwrap(); + } + + // NOTE: This doesn't work for PlutusV1, but I don't care. + if !language_views.is_empty() { + let mut views = language_views.to_vec(); + // TODO: Derive an Ord instance in Pallas. + views.sort_by(|(a, _), (b, _)| match (a, b) { + (Language::PlutusV3, Language::PlutusV3) => Ordering::Equal, + (Language::PlutusV3, _) => Ordering::Greater, + (_, Language::PlutusV3) => Ordering::Less, + + (Language::PlutusV2, Language::PlutusV2) => Ordering::Equal, + (Language::PlutusV2, _) => Ordering::Greater, + (_, Language::PlutusV2) => Ordering::Less, + + (Language::PlutusV1, Language::PlutusV1) => Ordering::Equal, + }); + cbor::encode(NonEmptyKeyValuePairs::Def(views), &mut preimage).unwrap() + } + + Some(Hasher::<256>::hash(&preimage)) +} + +pub fn default_transaction_body() -> TransactionBody { + TransactionBody { + auxiliary_data_hash: None, + certificates: None, + collateral: None, + collateral_return: None, + donation: None, + fee: 0, + inputs: Set::from(vec![]), + mint: None, + network_id: None, + outputs: vec![], + proposal_procedures: None, + reference_inputs: None, + required_signers: None, + script_data_hash: None, + total_collateral: None, + treasury_value: None, + ttl: None, + validity_interval_start: None, + voting_procedures: None, + withdrawals: None, + } +} + +pub fn default_witness_set() -> WitnessSet { + WitnessSet { + bootstrap_witness: None, + native_script: None, + plutus_data: None, + plutus_v1_script: None, + plutus_v2_script: None, + plutus_v3_script: None, + redeemer: None, + vkeywitness: None, + } +} + +// Build a transaction by repeatedly executing some building logic with different fee and execution +// units settings. Stops when a fixed point is reached. The final transaction has corresponding +// fees and execution units. +pub fn build_transaction(params: &BuildParams, resolved_inputs: &[ResolvedInput], with: F) -> Tx +where + F: Fn(u64, &[ExUnits]) -> Tx, +{ + let empty_ex_units = || { + vec![ + ExUnits { mem: 0, steps: 0 }, + ExUnits { mem: 0, steps: 0 }, + ExUnits { mem: 0, steps: 0 }, + ExUnits { mem: 0, steps: 0 }, + ] + }; + + let mut fee = 0; + let mut ex_units = empty_ex_units(); + let mut tx; + let mut attempts = 0; + loop { + tx = with(fee, &ex_units[..]); + + // Convert to minted_tx... + let mut serialized_tx = Vec::new(); + cbor::encode(&tx, &mut serialized_tx).unwrap(); + + let mut calculated_ex_units = if resolved_inputs.is_empty() { + empty_ex_units() + } else { + // Compute execution units + let minted_tx = cbor::decode(&serialized_tx).unwrap(); + eval_phase_two( + &minted_tx, + resolved_inputs, + None, + None, + &SlotConfig::default(), + false, + |_| (), + ) + .expect("script evaluation failed") + .into_iter() + .map(|r| r.0.ex_units) + .collect::>() + }; + + let estimated_fee = { + // NOTE: This is a best effort to estimate the number of signatories since signatures + // will add an overhead to the fee. Yet, if inputs are locked by native scripts each + // requiring multiple signatories, this will unfortunately fall short. + // + // For similar reasons, it will also over-estimate fees by a small margin for every + // script-locked inputs that do not require signatories. + // + // This is however *acceptable* in our context. + let num_signatories = tx.transaction_body.inputs.len() + + tx.transaction_body + .required_signers + .as_ref() + .map(|xs| xs.len()) + .unwrap_or(0); + + params.fee_constant + + params.fee_coefficient + * (5 // cbor overhead + //+ 16 * ex_units.len() // Redeemers. probably not needed. + + 102 * num_signatories // Reference?! + + serialized_tx.len()) as u64 + + total_execution_cost(params, &ex_units) + }; + + calculated_ex_units.extend(empty_ex_units()); + attempts += 1; + + // Check if we've reached a fixed point, or start over. + if fee >= estimated_fee + && calculated_ex_units + .iter() + .zip(ex_units) + .all(|(l, r)| l.eq(&r)) + { + break; + } else if attempts >= 3 { + panic!("failed to build transaction: did not converge after three attempts."); + } else { + ex_units = calculated_ex_units; + fee = estimated_fee; + } + } + tx +} + +pub fn expect_post_alonzo(output: &TransactionOutput) -> &PostAlonzoTransactionOutput { + if let TransactionOutput::PostAlonzo(ref o) = output { + o + } else { + panic!("expected PostAlonzo output but got a legacy one.") + } +} diff --git a/src/tx/send.rs b/src/tx/send.rs new file mode 100644 index 0000000..728060d --- /dev/null +++ b/src/tx/send.rs @@ -0,0 +1,80 @@ +use anyhow::Result; +use minicbor::encode; +use pallas_addresses::{Address, ShelleyAddress, ShelleyDelegationPart}; +use pallas_primitives::{ + conway::{ + PostAlonzoTransactionOutput, TransactionBody, TransactionInput, TransactionOutput, Tx, + Value, WitnessSet, + }, + Coin, Nullable, Set, +}; + +use crate::{cardano::cardano::Cardano, wallet::Wallet}; + +use super::{ + context::TxContext, + plutus::{ + build_transaction, default_transaction_body, default_witness_set, expect_post_alonzo, + from_network, into_outputs, value_subtract_lovelace, BuildParams, + }, +}; + +pub async fn send(ctx: TxContext, outputs: Vec<(Address, u64)>) -> Result +where + T: Cardano, +{ + let available_utxos = ctx.available_utxos().await.unwrap(); + let wallet_address = ctx.wallet_address(); + + let mut tx = build_transaction( + &ctx.build_parameters().await, + &available_utxos, + |fee, ex_units| { + let inputs = available_utxos + .iter() + .map(|u| u.input.clone()) + .collect::>(); + let x = expect_post_alonzo(&available_utxos[0].output); + let mut outputs = vec![]; + // let mut outputs = outputs.iter().map(|(addr, amount)| { + // TransactionOutput::PostAlonzoTransactionOutput { + // address: addr, + // value: Value::Coin(amount * 1_000_000) , + // datum_option: None, + // script_ref: None, + // } + // }).collect::>(); + outputs.push( + // Change + PostAlonzoTransactionOutput { + address: wallet_address.to_vec().into(), + value: value_subtract_lovelace(x.value.clone(), fee).expect("not enough fuel"), + datum_option: None, + script_ref: None, + }, + ); + + // ----- Put it all together + Tx { + transaction_body: TransactionBody { + inputs: Set::from(inputs), + network_id: Some(from_network(ctx.cardano.network_id())), + outputs: into_outputs(outputs), + fee, + ..default_transaction_body() + }, + transaction_witness_set: WitnessSet { + ..default_witness_set() + }, + success: true, + auxiliary_data: Nullable::Null, + } + }, + ); + ctx.wallet.sign(&mut tx); + let mut serialized_tx = Vec::new(); + println!("{:#?}", tx); + encode(&tx, &mut serialized_tx).unwrap(); + let tx_hash = ctx.cardano.submit(serialized_tx).await?; + Ok(tx_hash) +} diff --git a/src/tx/torso.rs b/src/tx/torso.rs new file mode 100644 index 0000000..c2ae735 --- /dev/null +++ b/src/tx/torso.rs @@ -0,0 +1,51 @@ +// /// Torso: the main part of the body relevant to tx building. + +// use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart}; +// use pallas_codec::{ +// minicbor as cbor, +// utils::{NonEmptyKeyValuePairs, NonEmptySet, Set}, +// }; +// use pallas_crypto::hash::{Hash, Hasher}; +// use pallas_primitives::{conway::{ +// AssetName, Constr, ExUnits, Language, Multiasset, NetworkId, PlutusData, +// PostAlonzoTransactionOutput, PseudoTransactionOutput, RedeemerTag, RedeemersKey, +// RedeemersValue, TransactionBody, TransactionInput, TransactionOutput, Tx, Value, WitnessSet, +// }, MaybeIndefArray, PolicyId}; +// use std::{cmp::Ordering, collections::HashMap, str::FromStr}; +// use uplc::tx::{eval_phase_two, ResolvedInput, SlotConfig}; + +// struct TransactionBody { +// inputs: Set, +// outputs: Vec, +// fee: Coin, +// ttl: Option, +// certificates: Option>, +// withdrawals: Option>, +// auxiliary_data_hash: Option, +// validity_interval_start: Option, +// mint: Option>, +// script_data_hash: Option>, +// collateral: Option>, +// required_signers: Option, +// network_id: Option, +// collateral_return: Option, +// total_collateral: Option, +// reference_inputs: Option>, +// voting_procedures: Option, +// proposal_procedures: Option>, +// treasury_value: Option, +// donation: Option, +// } + +// pub struct Torso { +// inputs: Vec, +// reference_inputs: Vec, +// outputs: Vec, +// mint: HashMap>, +// voting_procedures: Vec +// collateral: Vec, +// required_signers: Vec, +// } +// +// pub struct WitnessPart { +// } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..9f5c5d8 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,22 @@ +use anyhow::{anyhow, Result}; + +pub fn v2a(v: Vec) -> Result<[T; N]> { + v.try_into() + .map_err(|v: Vec| anyhow!("Expected a Vec of length {}, but got {}", N, v.len())) +} + +pub fn concat(l: &[T], r: &[T]) -> Vec { + let mut n = l.to_vec(); + n.extend(r.iter().cloned()); + return n; +} + +pub fn unzip(zipped: Vec<(A, B)>) -> (Vec, Vec) { + let mut va: Vec = Vec::with_capacity(zipped.len()); + let mut vb: Vec = Vec::with_capacity(zipped.len()); + for (a, b) in zipped.into_iter() { + va.push(a); + vb.push(b); + } + (va, vb) +} diff --git a/src/wallet.rs b/src/wallet.rs new file mode 100644 index 0000000..796a58a --- /dev/null +++ b/src/wallet.rs @@ -0,0 +1,78 @@ +use std::collections::HashMap; + +use cryptoxide::hashing::blake2b_256; +use minicbor::encode; +use pallas_addresses::ShelleyPaymentPart; +use pallas_crypto::hash::{Hash, Hasher}; +use pallas_crypto::key::ed25519::Signature; +use pallas_crypto::{self, key::ed25519::SecretKey}; +use pallas_primitives::conway::{Tx, VKeyWitness}; + +use crate::tx::plutus::non_empty_set; +use crate::utils::v2a; + +use rand::{rngs::OsRng, TryRngCore}; + +const PREFIX: &str = "wallet_"; + +pub struct Wallet { + pub skey: [u8; 32], +} + +impl Wallet { + pub fn vkey(&self) -> [u8; 32] { + SecretKey::from(self.skey).public_key().into() + } + + pub fn key_hash(&self) -> Hash<28> { + Hasher::<224>::hash(SecretKey::from(self.skey).public_key().as_ref()) + } + + pub fn payment_credential(&self) -> ShelleyPaymentPart { + ShelleyPaymentPart::Key(self.key_hash()) + } + + pub fn sign_hash(&self, h: &[u8; 32]) -> Signature { + SecretKey::from(self.skey).sign(h) + } + + pub fn sign(&self, tx: &mut Tx) { + let mut msg = Vec::new(); + encode(&tx.transaction_body, &mut msg).unwrap(); + let tx_hash = blake2b_256(&msg); + let sig = self.sign_hash(&tx_hash); + tx.transaction_witness_set.vkeywitness = non_empty_set(vec![VKeyWitness { + vkey: self.vkey().to_vec().into(), + signature: sig.as_ref().to_vec().into(), + }]) + } +} + +pub fn from_env(env: &HashMap) -> Wallet { + let wallet_env: HashMap = env + .iter() + .filter_map(|(k, v)| k.strip_prefix(PREFIX).map(|k| (k.to_string(), v.clone()))) + .collect(); + let raw = wallet_env.get("key").expect("wallet key not found"); + let skey = parse_raw_skey(raw); + Wallet { skey } +} + +pub fn generate() -> [u8; 32] { + let mut key = [0u8; 32]; + OsRng.try_fill_bytes(&mut key).unwrap(); + key +} + +fn parse_raw_skey(raw: &str) -> [u8; 32] { + // FIXME :: Not tested + if raw.len() == 64 { + // Assume hex + v2a(hex::decode(raw).expect("expected hex")).expect("wrong length") + } else if raw.len() == 70 { + // Assume Bech + v2a(bech32::decode(raw).unwrap().1).expect("wrong length") + } else { + panic!("Not supported") + } +}