diff --git a/.github/workflows/fixtures.yml b/.github/workflows/fixtures.yml index d2cfd3ab..669ed99f 100644 --- a/.github/workflows/fixtures.yml +++ b/.github/workflows/fixtures.yml @@ -66,6 +66,7 @@ jobs: shell: bash run: | cd ${{ matrix.build-dir }} + simplex install simplex build test -f src/artifacts/mod.rs @@ -130,6 +131,7 @@ jobs: shell: bash run: | cd ${{ matrix.build-dir }} + simplex install simplex build test -f src/artifacts/mod.rs diff --git a/Cargo.lock b/Cargo.lock index aa22c543..9c047783 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -69,19 +69,29 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ "object", ] +[[package]] +name = "ariadne" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "arrayvec" version = "0.7.8" @@ -96,9 +106,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,9 +116,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", @@ -151,17 +161,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ "bitcoin_hashes", - "rand", - "rand_core", + "rand 0.8.8", + "rand_core 0.6.4", "serde", "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcoin" -version = "0.32.101" +version = "0.32.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed8ccb78a9ff7a6fbb90e2fb9b8588b4a9928d49d8af3cb789108a84ea6b0ce" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" dependencies = [ "base58ck", "base64 0.21.7", @@ -177,21 +202,20 @@ dependencies = [ [[package]] name = "bitcoin-consensus-encoding" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", ] [[package]] name = "bitcoin-internals" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a30a22d1f112dde8e16be7b45c63645dc165cef254f835b3e1e9553e485cfa64" -dependencies = [ - "hex-conservative 0.3.2", -] +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" [[package]] name = "bitcoin-io" @@ -274,9 +298,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -298,9 +322,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "serde_core", @@ -320,9 +344,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.66" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -338,9 +362,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chumsky" @@ -358,9 +382,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -368,9 +392,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -380,14 +404,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] @@ -483,7 +507,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "libc", "objc2", @@ -509,9 +533,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" dependencies = [ "serde", ] @@ -584,15 +608,21 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" @@ -629,6 +659,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.3" @@ -637,7 +679,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", ] [[package]] @@ -648,20 +690,20 @@ checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.14", + "regex-automata 0.4.18", "regex-syntax 0.8.11", ] @@ -671,7 +713,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "ignore", "walkdir", ] @@ -716,9 +758,9 @@ dependencies = [ [[package]] name = "hex-conservative" -version = "0.3.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830e599c2904b08f0834ee6337d8fe8f0ed4a63b5d9e7a7f49c0ffa06d08d360" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" dependencies = [ "arrayvec", ] @@ -749,15 +791,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.27" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe112b004901c62c2faa11f4f75e9864e0cc5af8da71c9115d184a3aa888749f" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.14", + "regex-automata 0.4.18", "same-file", "walkdir", "winapi-util", @@ -828,9 +870,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -846,9 +888,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" @@ -918,12 +960,21 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "libc", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "objc2" version = "0.6.4" @@ -941,9 +992,9 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "object" -version = "0.37.3" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "memchr", ] @@ -974,9 +1025,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "ppv-lite86" @@ -994,37 +1045,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.13.1", + "num-traits", + "rand 0.9.5", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax 0.8.11", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -1033,13 +1115,23 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -1049,7 +1141,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[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.5", ] [[package]] @@ -1061,6 +1163,24 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "regex-automata" version = "0.3.9" @@ -1074,9 +1194,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1115,7 +1235,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -1128,7 +1248,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -1137,9 +1257,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -1152,18 +1272,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -1177,6 +1297,18 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "same-file" version = "1.0.6" @@ -1193,7 +1325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand", + "rand 0.8.8", "secp256k1-sys", "serde", ] @@ -1214,7 +1346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" dependencies = [ "bitcoin-private", - "rand", + "rand 0.8.8", "secp256k1", "secp256k1-zkp-sys", "serde", @@ -1230,11 +1362,17 @@ dependencies = [ "secp256k1-sys", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1242,22 +1380,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] @@ -1328,10 +1466,11 @@ dependencies = [ [[package]] name = "simplicityhl" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361316795ec753230c421d964ab60940d20d5252c2d39272452528832c9e0ec5" +checksum = "ae77168fa3367d234b11bb3999cbd6e04d7180db0b9e11f83de259528e3b23d1" dependencies = [ + "ariadne", "base64 0.21.7", "chumsky", "clap", @@ -1339,6 +1478,7 @@ dependencies = [ "getrandom 0.2.17", "itertools", "miniscript", + "semver", "serde", "serde_json", "simplicity-lang", @@ -1357,7 +1497,7 @@ dependencies = [ "serde", "serde_json", "simplicityhl", - "syn", + "syn 2.0.119", "thiserror", "toml 0.9.12+spec-1.1.0", "toml_edit", @@ -1389,7 +1529,7 @@ version = "0.0.10" dependencies = [ "smplx-build", "smplx-test", - "syn", + "syn 2.0.119", ] [[package]] @@ -1417,6 +1557,7 @@ dependencies = [ "elements-miniscript", "hex", "minreq 3.0.0", + "rand_core 0.9.5", "serde", "serde_json", "sha2", @@ -1429,6 +1570,8 @@ name = "smplx-std" version = "0.0.10" dependencies = [ "either", + "rand 0.9.5", + "rand_core 0.9.5", "serde", "simplicityhl", "smplx-macros", @@ -1443,12 +1586,14 @@ version = "0.0.10" dependencies = [ "electrsd", "proc-macro2", + "proptest", "quote", + "rand 0.9.5", "serde", "simplicityhl", "smplx-regtest", "smplx-sdk", - "syn", + "syn 2.0.119", "thiserror", "toml 0.9.12+spec-1.1.0", ] @@ -1467,9 +1612,9 @@ dependencies = [ [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -1492,9 +1637,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -1503,9 +1659,9 @@ dependencies = [ [[package]] name = "target-triple" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" +checksum = "c3a6bfce3d99adfa72d24750a61f782f3036a81e7f86d8841ee1326deaebd171" [[package]] name = "tempfile" @@ -1531,29 +1687,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -1581,9 +1737,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -1591,7 +1747,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -1614,37 +1770,37 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "trybuild" -version = "1.0.117" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0710d4dfbeae4f9c390baa784c49858a7468fa433f3fe5d0ec5ebef651cf59f9" +checksum = "1e605bf6b39357663d8ba4e984f8be8da8df6bb32e81031d6889024ea8fd68e4" dependencies = [ "glob", "serde", @@ -1652,7 +1808,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", ] [[package]] @@ -1661,6 +1817,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1682,6 +1844,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[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" @@ -1700,6 +1868,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -1716,6 +1893,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1738,7 +1924,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -1760,7 +1946,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1776,9 +1962,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -1909,31 +2095,43 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1944,6 +2142,6 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index f561994a..3619dbba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ smplx-sdk = { path = "./crates/sdk", version = "0.0.10", default-features = fals smplx-std = { path = "./crates/simplex", version = "0.0.10" } smplx-wasm = { path = "./crates/wasm", version = "0.0.10" } +rand_core = { version = "0.9.5" } +rand = { version = "0.9.4" } serde = { version = "1.0.228", features = ["derive"] } hex = { version = "0.4.3" } hmac = { version = "0.12.1" } @@ -28,7 +30,7 @@ minreq = { version = "3.0.0", features = ["https", "json-using-serde"] } electrsd = { version = "0.29.0", features = ["legacy"] } bitcoincore-rpc = { version = "0.19.0" } -simplicityhl = { version = "0.6.0" } +simplicityhl = { version = "0.7.2" } [workspace.lints.rust] rust_2018_idioms = "warn" diff --git a/crates/build/Cargo.toml b/crates/build/Cargo.toml index adf63903..f2939d63 100644 --- a/crates/build/Cargo.toml +++ b/crates/build/Cargo.toml @@ -21,5 +21,5 @@ quote = { version = "1.0.44" } pathdiff = { version = "0.2.3" } prettyplease = { version = "0.2.37" } glob = { version = "0.3.3"} -globwalk = { version = "0.9.1"} +globwalk = { version = "0.9.1" } serde_json = { version = "1.0.151" } diff --git a/crates/build/src/generator.rs b/crates/build/src/generator.rs index 0f7d6091..80f60c66 100644 --- a/crates/build/src/generator.rs +++ b/crates/build/src/generator.rs @@ -19,8 +19,7 @@ use simplicityhl::source::CanonSourceFile; use crate::contract_id::ContractId; use crate::macros::codegen::{ - convert_contract_name_to_contract_module, convert_contract_name_to_contract_source_const, - convert_contract_name_to_struct_name, + construct_program_name, convert_contract_name_to_contract_module, convert_contract_name_to_contract_source_const, }; use crate::macros::parse::SimfContent; @@ -170,7 +169,7 @@ impl ArtifactsGenerator { ) .map_err(|diags| BuildError::DryRun(diags.to_string()))?; let flattened = TemplateProgram::flatten(canon_source_file, &dependency_map, &UnstableFeatures::all()) - .map_err(BuildError::Flattening)?; + .map_err(|diags| BuildError::Flattening(diags.to_string()))?; Ok(SourceEntry { cmr: ContractId::from_template(&template)?, @@ -279,22 +278,19 @@ impl ArtifactsGenerator { } fn generate_simf_binding_code(contract_name: &str, target_simf: &Path) -> Result { - let program_name = { - let base_name = convert_contract_name_to_struct_name(contract_name); - format_ident!("{base_name}Program") - }; - + let program_name = construct_program_name(contract_name); let include_simf_source_const = convert_contract_name_to_contract_source_const(contract_name); let include_simf_module = convert_contract_name_to_contract_module(contract_name); let target_simf_str = target_simf.to_string_lossy().into_owned(); let code = quote! { use simplex::include_simf; - use simplex::program::{ArgumentsTrait, Program}; + use simplex::program::{Program}; use simplex::provider::SimplicityNetwork; use simplex::simplicityhl::elements::Script; use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; + #[derive(Clone)] pub struct #program_name { program: Program, } @@ -303,9 +299,9 @@ impl ArtifactsGenerator { pub const SOURCE: &'static str = #include_simf_module::#include_simf_source_const; #[must_use] - pub fn new(arguments: &impl ArgumentsTrait) -> Self { + pub fn new(arguments: impl Into) -> Self { Self { - program: Program::new(Self::SOURCE, arguments), + program: Program::new(Self::SOURCE, arguments.into()), } } diff --git a/crates/build/src/macros/codegen.rs b/crates/build/src/macros/codegen.rs index 2936e9be..923b6a91 100644 --- a/crates/build/src/macros/codegen.rs +++ b/crates/build/src/macros/codegen.rs @@ -8,6 +8,7 @@ use crate::macros::types::RustType; pub struct SimfContractMeta { pub contract_source_const_name: proc_macro2::Ident, + pub program_struct_name: proc_macro2::Ident, pub args_struct: WitnessStruct, pub witness_struct: WitnessStruct, pub simf_content: SimfContent, @@ -26,6 +27,11 @@ pub struct GeneratedWitnessTokens { pub struct_impl: proc_macro2::TokenStream, } +pub struct GeneratedProgramTraitHelperTokens { + pub imports: proc_macro2::TokenStream, + pub helper_impls: proc_macro2::TokenStream, +} + pub struct WitnessField { witness_simf_name: String, struct_rust_field: proc_macro2::Ident, @@ -47,15 +53,41 @@ impl SimfContractMeta { let witness_struct = WitnessStruct::generate_witness_struct(&simf_content.contract_name, &abi_meta.witness_types)?; let contract_source_const_name = convert_contract_name_to_contract_source_const(&simf_content.contract_name); - + let program_struct_name = construct_program_name(&simf_content.contract_name); Ok(SimfContractMeta { contract_source_const_name, + program_struct_name, args_struct, witness_struct, simf_content, abi_meta, }) } + + /// Generates code necessary for creating mutant testing using simplex. + pub fn generate_program_trait_helpers_impl(&self) -> syn::Result { + let args_struct_name = &self.args_struct.struct_name; + let program_name = &self.program_struct_name; + + let program_helpers_impl = quote! { + impl ProgramFactory<#program_name> for #program_name { + fn instantiate_program(args: impl Into) -> Box<#program_name> { + Box::new(#program_name::new(args)) + } + } + }; + + Ok(GeneratedProgramTraitHelperTokens { + imports: quote! { + use super::{super::#program_name, #args_struct_name}; + use simplex::program::{Program, ProgramFactory}; + use simplex::simplicityhl::{Arguments}; + }, + helper_impls: quote! { + #program_helpers_impl + }, + }) + } } impl WitnessField { @@ -105,6 +137,7 @@ impl WitnessStruct { proc_macro2::TokenStream, proc_macro2::TokenStream, ) = self.generate_from_args_conversion_with_param_name("args"); + let rand_mapping: proc_macro2::TokenStream = self.generate_rand_mapping(); let default_mapping: proc_macro2::TokenStream = self.generate_default_mapping(); Ok(GeneratedArgumentTokens { @@ -117,28 +150,34 @@ impl WitnessStruct { use simplex::simplicityhl::types::TypeConstructible; use simplex::simplicityhl::value::ValueConstructible; use simplex::program::ArgumentsTrait; + use simplex::rand_core::{RngCore}; + use simplex::rand::Rng; }, struct_token_stream: quote! { #generated_struct }, struct_impl: quote! { impl #struct_name { - /// Build struct from Simplicity Arguments. + /// Build struct from Simplicity `Arguments`. /// /// # Errors /// - /// Returns error if any required witness is missing, has wrong type, or has invalid value. + /// Returns error if any required witness is missing, has the wrong type, or has an invalid value. pub fn from_arguments(args: &Arguments) -> Result { #arguments_conversion_from_args_map Ok(#struct_to_return) } + /// Generate a random Arguments struct instance using the provided RNG. + pub fn generate_arguments_raw(rng: &mut R) -> Self + { + #rand_mapping + } } impl simplex::program::ArgumentsTrait for #struct_name { /// Build Simplicity arguments for contract instantiation. - #[must_use] fn build_arguments(&self) -> simplex::simplicityhl::Arguments { simplex::simplicityhl::Arguments::from(HashMap::from([ #(#tuples),* @@ -165,11 +204,24 @@ impl WitnessStruct { } } + impl simplex::program::RandomArguments for #struct_name { + fn generate_arguments(rng: &mut dyn RngCore) -> simplex::simplicityhl::Arguments + { + Self::generate_arguments_raw(rng).build_arguments() + } + } + impl core::default::Default for #struct_name { fn default() -> Self { #default_mapping } } + + impl From<#struct_name> for simplex::simplicityhl::Arguments { + fn from(val: #struct_name) -> simplex::simplicityhl::Arguments { + val.build_arguments() + } + } }, }) } @@ -187,6 +239,7 @@ impl WitnessStruct { proc_macro2::TokenStream, ) = self.generate_from_args_conversion_with_param_name("witness"); let default_mapping: proc_macro2::TokenStream = self.generate_default_mapping(); + let rand_mapping: proc_macro2::TokenStream = self.generate_rand_mapping(); Ok(GeneratedWitnessTokens { imports: quote! { @@ -198,13 +251,15 @@ impl WitnessStruct { use simplex::simplicityhl::types::TypeConstructible; use simplex::simplicityhl::value::ValueConstructible; use simplex::program::WitnessTrait; + use simplex::rand_core::{RngCore}; + use simplex::rand::Rng; }, struct_token_stream: quote! { #generated_struct }, struct_impl: quote! { impl #struct_name { - /// Build struct from Simplicity WitnessValues. + /// Build struct from Simplicity `WitnessValues`. /// /// # Errors /// @@ -214,11 +269,16 @@ impl WitnessStruct { Ok(#struct_to_return) } + + /// Generate a random Witness struct instance using the provided RNG. + pub fn generate_witness_raw(rng: &mut R) -> Self + { + #rand_mapping + } } impl simplex::program::WitnessTrait for #struct_name { /// Build Simplicity witness values for contract execution. - #[must_use] fn build_witness(&self) -> simplex::simplicityhl::WitnessValues { simplex::simplicityhl::WitnessValues::from(HashMap::from([ #(#tuples),* @@ -245,11 +305,24 @@ impl WitnessStruct { } } + impl simplex::program::RandomWitness for #struct_name { + fn generate_witness(rng: &mut dyn RngCore) -> simplex::simplicityhl::WitnessValues + { + Self::generate_witness_raw(rng).build_witness() + } + } + impl core::default::Default for #struct_name { fn default() -> Self { #default_mapping } } + + impl From<#struct_name> for simplex::simplicityhl::WitnessValues { + fn from(val: #struct_name) -> simplex::simplicityhl::WitnessValues { + val.build_witness() + } + } }, }) } @@ -300,6 +373,24 @@ impl WitnessStruct { } } + fn generate_rand_mapping(&self) -> proc_macro2::TokenStream { + let name = format_ident!("{}", self.struct_name); + let fields: Vec = self + .witness_values + .iter() + .map(|field| { + let field_name = format_ident!("{}", field.struct_rust_field); + let field_default_value = field.rust_type.get_random_value(); + quote! { #field_name: #field_default_value } + }) + .collect(); + quote! { + #name { + #(#fields),* + } + } + } + fn generate_default_mapping(&self) -> proc_macro2::TokenStream { let name = format_ident!("{}", self.struct_name); let fields: Vec = self @@ -367,6 +458,11 @@ impl WitnessStruct { } } +pub fn construct_program_name(contract_name: &str) -> proc_macro2::Ident { + let base_name = convert_contract_name_to_struct_name(contract_name); + format_ident!("{base_name}Program") +} + pub fn convert_contract_name_to_struct_name(contract_name: &str) -> String { let words: Vec = contract_name .split('_') diff --git a/crates/build/src/macros/core.rs b/crates/build/src/macros/core.rs index 12f6d5b5..e50d29b2 100644 --- a/crates/build/src/macros/core.rs +++ b/crates/build/src/macros/core.rs @@ -7,7 +7,8 @@ use simplicityhl::ast::ElementsJetHinter; use simplicityhl::{AbiMeta, TemplateProgram, UnstableFeatures}; use super::codegen::{ - GeneratedArgumentTokens, GeneratedWitnessTokens, SimfContractMeta, convert_contract_name_to_contract_module, + GeneratedArgumentTokens, GeneratedProgramTraitHelperTokens, GeneratedWitnessTokens, SimfContractMeta, + convert_contract_name_to_contract_module, }; use super::parse::{SimfContent, SynFilePath}; @@ -27,6 +28,7 @@ fn expand_inner(simf_content: SimfContent, meta: AbiMeta) -> Result Result Result> { .generate_abi_meta()?, ) } + +fn construct_program_trait_helpers(derived_meta: &SimfContractMeta) -> syn::Result { + let GeneratedProgramTraitHelperTokens { imports, helper_impls } = + derived_meta.generate_program_trait_helpers_impl()?; + + Ok(quote! { + mod program_helpers { + #imports + + #helper_impls + } + }) +} diff --git a/crates/build/src/macros/types.rs b/crates/build/src/macros/types.rs index 00fbcb68..8bca1f8e 100644 --- a/crates/build/src/macros/types.rs +++ b/crates/build/src/macros/types.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use quote::quote; +use quote::{format_ident, quote}; use simplicityhl::ResolvedType; @@ -60,6 +60,61 @@ impl RustTypeContext { } impl RustType { + pub fn get_random_value(&self) -> proc_macro2::TokenStream { + match self { + RustType::Bool => quote! { rng.random() }, + RustType::U1 => quote! { rng.random::() as u8 }, + RustType::U2 => quote! { rng.random::() & 0x03 }, + RustType::U4 => quote! { rng.random::() & 0x0F }, + RustType::U8 | RustType::U16 | RustType::U32 | RustType::U64 | RustType::U128 => quote! { rng.random() }, + RustType::U256Array => quote! { rng.random() }, + RustType::Array(element, size) => { + let elements = vec![element.get_random_value(); *size]; + quote! { [#(#elements),*] } + } + RustType::Tuple(elements) => { + let element_values: Vec<_> = elements.iter().map(RustType::get_random_value).collect(); + quote! { (#(#element_values),*) } + } + RustType::Either(left, right) => { + let left_val = left.get_random_value(); + let right_val = right.get_random_value(); + quote! { if rng.random() { simplex::either::Either::Left(#left_val) } else { simplex::either::Either::Right(#right_val) } } + } + RustType::Option(inner) => { + let inner_val = inner.get_random_value(); + quote! { if rng.random() { Some(#inner_val) } else { None } } + } + RustType::List(element, size) => { + let (generated_fn, generated_fn_name) = { + let generated_fn_name = format_ident!("__gen_list_path"); + let rust_ret_type = element.to_type_token_stream(); + let rand_element_generation = element.get_random_value(); + + let generated_fn = quote! { + fn #generated_fn_name(rng: &mut R) -> #rust_ret_type{ + #rand_element_generation + } + }; + (generated_fn, generated_fn_name) + }; + + quote! { + { + #generated_fn + + let random_size: u32 = rng.random_range(0..#size as u32); + let mut res = Vec::with_capacity(random_size as usize); + for s in 0..random_size { + res.push(#generated_fn_name(rng)); + } + res + } + } + } + } + } + pub fn get_default_value(&self) -> proc_macro2::TokenStream { match self { RustType::Bool => quote! { Default::default() }, diff --git a/crates/cli/assets/Simplex.default.toml b/crates/cli/assets/Simplex.default.toml index 5ab8f8bb..3286a810 100644 --- a/crates/cli/assets/Simplex.default.toml +++ b/crates/cli/assets/Simplex.default.toml @@ -25,6 +25,12 @@ # url = "" # network = "" +# [test.fuzz] +# cases = 10000 +# max_shrink_iters = 100 +# max_global_rejects = 100 +# max_local_rejects = 100 + # [test.rpc] # url = "" # username = "" diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index d3c26427..bd2947e3 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -83,11 +83,11 @@ impl Cli { Ok(Build::run(&loaded_config.build, &loaded_config.dependencies)?) } - Command::Clean => { + Command::Clean { flags } => { let config_path = Config::get_default_path()?; let loaded_config = Config::load(&config_path)?; - Ok(Clean::run(&loaded_config.build)?) + Ok(Clean::run(&loaded_config.build.out_dir, flags)?) } } } diff --git a/crates/cli/src/commands/clean.rs b/crates/cli/src/commands/clean.rs index 4d3664c4..bbf08758 100644 --- a/crates/cli/src/commands/clean.rs +++ b/crates/cli/src/commands/clean.rs @@ -1,7 +1,10 @@ +use std::path::Path; use std::{fmt::Display, fs, path::PathBuf}; -use smplx_build::{ArtifactsResolver, BuildConfig}; +use smplx_build::ArtifactsResolver; +use smplx_build::config::DEFAULT_DEPENDENCY_DIR; +use crate::commands::CleanFlags; use crate::commands::error::CleanError; use crate::commands::error::CommandError; @@ -12,41 +15,42 @@ pub struct DeletedItems(Vec); impl Clean { /// Cleans up generated artifacts from the project. /// - /// This method removes compiled files and output directories based on the provided configuration. + /// Resolves which directories to remove based on `flags` and deletes them. /// /// # Errors - /// Returns a `CommandError` if it fails to resolve the artifacts directory or if an error occurs while removing the directories. - pub fn run(config: &BuildConfig) -> Result<(), CommandError> { - let deleted_files = Self::delete_files(config)?; + /// Returns a `CommandError` if the artifacts directory cannot be resolved + /// or if removing any directory fails. + pub fn run(artifacts: &impl AsRef, flags: &CleanFlags) -> Result<(), CommandError> { + let artifacts_dir = ArtifactsResolver::resolve_local_dir(artifacts) + .map_err(|err| CleanError::ResolveOutDir(err.to_string()))?; + + let mut to_remove = vec![artifacts_dir]; + if flags.remove_all { + let deps_dir = ArtifactsResolver::resolve_local_dir(&DEFAULT_DEPENDENCY_DIR) + .map_err(|err| CleanError::ResolveOutDir(err.to_string()))?; + + to_remove.push(deps_dir); + } - println!("Deleted files: {deleted_files}"); + let deleted = Self::delete_files(&to_remove)?; + println!("Deleted files: {deleted}"); Ok(()) } - fn delete_files(config: &BuildConfig) -> Result { - let mut deleted_items = Vec::with_capacity(1); - let generated_artifacts = Self::remove_artifacts(config)?; - - if let Some(artifacts_dir) = generated_artifacts { - deleted_items.push(artifacts_dir); - } + fn delete_files(paths: &[PathBuf]) -> Result { + let mut deleted = Vec::with_capacity(paths.len()); - Ok(DeletedItems(deleted_items)) - } - - fn remove_artifacts(config: &BuildConfig) -> Result, CleanError> { - let output_dir = ArtifactsResolver::resolve_local_dir(&config.out_dir) - .map_err(|e| CleanError::ResolveOutDir(e.to_string()))?; + for path in paths { + if !path.exists() { + continue; + } - let res = if output_dir.exists() { - fs::remove_dir_all(&output_dir).map_err(|e| CleanError::RemoveOutDir(e, output_dir.clone()))?; - Some(output_dir) - } else { - None - }; + fs::remove_dir_all(path).map_err(|e| CleanError::RemoveOutDir(e, path.clone()))?; + deleted.push(path.clone()); + } - Ok(res) + Ok(DeletedItems(deleted)) } } diff --git a/crates/cli/src/commands/core.rs b/crates/cli/src/commands/core.rs index 3a2babfa..d9b66105 100644 --- a/crates/cli/src/commands/core.rs +++ b/crates/cli/src/commands/core.rs @@ -29,7 +29,10 @@ pub enum Command { /// Generates the simplicity contracts artifacts Build, /// Cleans Simplex artifacts in the current directory - Clean, + Clean { + #[command(flatten)] + flags: CleanFlags, + }, } #[allow(clippy::struct_excessive_bools)] @@ -67,4 +70,15 @@ pub struct TestFlags { /// Run non-simplex tests (may be used for running unit tests) #[arg(long = "no-simplex")] pub no_simplex: bool, + /// Perform fuzzing via simplex + #[arg(long = "fuzz")] + pub fuzz: bool, +} + +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Args, Clone)] +pub struct CleanFlags { + /// Remove all files created by Simplex, including installed dependencies + #[arg(long = "all")] + pub remove_all: bool, } diff --git a/crates/cli/src/commands/test.rs b/crates/cli/src/commands/test.rs index bb624526..ea9225b3 100644 --- a/crates/cli/src/commands/test.rs +++ b/crates/cli/src/commands/test.rs @@ -9,6 +9,7 @@ use super::error::CommandError; /// Nextest dsl variable to filter and use only simplex tests const SMPLX_NEXTEST_DSL_TEST_MARKER: &str = concat!("test(/", smplx_test_marker!(), "$/)"); +const SMPLX_NEXTEST_FUZZ_DSL_TEST_MARKER: &str = concat!("test(/", smplx_test_marker!(fuzz), "$/)"); const DEFAULT_THREADS_NUMBER: usize = 1; pub struct Test {} @@ -84,6 +85,8 @@ impl Test { let dsl_marker = if flags.no_simplex { format!("not {SMPLX_NEXTEST_DSL_TEST_MARKER}") + } else if flags.fuzz { + format!("not {SMPLX_NEXTEST_DSL_TEST_MARKER} and {SMPLX_NEXTEST_FUZZ_DSL_TEST_MARKER}") } else { SMPLX_NEXTEST_DSL_TEST_MARKER.into() }; diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 15136045..6842b40f 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -16,7 +16,17 @@ pub fn include_simf(tokenstream: TokenStream) -> TokenStream { pub fn test(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input as syn::ItemFn); - match smplx_test::macros::expand(args.into(), input) { + match smplx_test::macros::expand_test(args.into(), input) { + Ok(ts) => ts.into(), + Err(e) => e.to_compile_error().into(), + } +} + +#[proc_macro_attribute] +pub fn fuzz(args: TokenStream, input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(input as syn::ItemFn); + + match smplx_test::macros::expand_fuzz(args.into(), input) { Ok(ts) => ts.into(), Err(e) => e.to_compile_error().into(), } diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 2c5d6bdc..f319aa90 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -22,9 +22,10 @@ simplicityhl = { workspace = true } bitcoincore-rpc = { workspace = true, optional = true } serde = { workspace = true } hex = { workspace = true } +rand_core = { workspace = true } serde_json = { version = "1.0" } elements-miniscript = { version = "0.4", features = ["base64", "serde"] } bip39 = { version = "2.0.0", features = ["rand"] } dyn-clone = { version = "1.0.20" } -bitcoin_hashes = { version = "0.14.1" } +bitcoin_hashes = { version = "0.14.101" } diff --git a/crates/sdk/src/program/arguments.rs b/crates/sdk/src/program/arguments.rs index 61d734dd..9c891ad9 100644 --- a/crates/sdk/src/program/arguments.rs +++ b/crates/sdk/src/program/arguments.rs @@ -2,10 +2,17 @@ use dyn_clone::DynClone; use simplicityhl::Arguments; /// An interface for structs capable of generating static argument mapping for Simplicity programs. -/// See the `include_simc!()` macro, which generates automatic `ArgumentsTrait` implementation. +/// See the `include_simf!()` macro, which generates automatic `ArgumentsTrait` implementation. pub trait ArgumentsTrait: DynClone { /// Compiles and returns the bound `Arguments` dict required to instantiate a program. fn build_arguments(&self) -> Arguments; } dyn_clone::clone_trait_object!(ArgumentsTrait); + +/// An interface for the struct capable of generating proper `Arguments` mappings using the provided RNG. +/// See the ` include_simf!()` macro, which generates an automatic `ArgumentsTrait` implementation. +pub trait RandomArguments: ArgumentsTrait { + /// Generates a random `Arguments` instance using the provided RNG. + fn generate_arguments(rng: &mut dyn rand_core::RngCore) -> Arguments; +} diff --git a/crates/sdk/src/program/core.rs b/crates/sdk/src/program/core.rs index c37cb251..10406815 100644 --- a/crates/sdk/src/program/core.rs +++ b/crates/sdk/src/program/core.rs @@ -15,7 +15,6 @@ use simplicityhl::{CompiledProgram, UnstableFeatures}; use crate::global::GlobalConfig; use crate::program::logger::ProgramLogger; -use super::arguments::ArgumentsTrait; use super::error::ProgramError; use crate::provider::SimplicityNetwork; @@ -200,14 +199,53 @@ impl ProgramTrait for Program { } } +impl ProgramTrait for &T { + fn get_argument_types(&self) -> Result { + (*self).get_argument_types() + } + + fn get_witness_types(&self) -> Result { + (*self).get_witness_types() + } + + fn get_env( + &self, + pst: &PartiallySignedTransaction, + input_index: usize, + network: &SimplicityNetwork, + ) -> Result>, ProgramError> { + (*self).get_env(pst, input_index, network) + } + + fn execute( + &self, + pst: &PartiallySignedTransaction, + witness: &WitnessValues, + input_index: usize, + network: &SimplicityNetwork, + ) -> Result<(Arc, Value), ProgramError> { + (*self).execute(pst, witness, input_index, network) + } + + fn finalize( + &self, + pst: &PartiallySignedTransaction, + witness: &WitnessValues, + input_index: usize, + network: &SimplicityNetwork, + ) -> Result>, ProgramError> { + (*self).finalize(pst, witness, input_index, network) + } +} + impl Program { /// Creates a new instance of the struct with the provided source string and arguments. #[must_use] - pub fn new(source: impl Into>, arguments: &dyn ArgumentsTrait) -> Self { + pub fn new(source: impl Into>, arguments: impl Into) -> Self { Self { source: source.into(), pub_key: tr_unspendable_key(), - arguments: arguments.build_arguments(), + arguments: arguments.into(), storage: Vec::new(), include_debug_symbols: None, compiled: Arc::new(OnceLock::new()), @@ -347,7 +385,11 @@ impl Program { Ok(abi_meta.witness_types) } - fn load(&self) -> Result<&CompiledProgram, ProgramError> { + /// Compiles program by providing saved `Arguments`. + /// + /// # Errors + /// Retruns an error we have problems in a program compilation. + pub fn load(&self) -> Result<&CompiledProgram, ProgramError> { // Check cache first if let Some(compiled) = self.compiled.get() { return Ok(compiled); @@ -418,6 +460,14 @@ impl Program { } } +/// A trait for creating instances of a program. The `ProgramFactory` trait defines a mechanism +/// for constructing and returning a program instance of a type that implements `AsRef`. +/// Even only by generic struct name we have a possibility to create an instance of a program. +pub trait ProgramFactory + Sized> { + /// Instantiates a program instance with the given arguments. + fn instantiate_program(args: impl Into) -> Box

; +} + #[cfg(test)] mod tests { use simplicityhl::{ @@ -427,6 +477,8 @@ mod tests { use super::*; + use crate::program::ArgumentsTrait; + // simplicityhl/examples/cat.simf const DUMMY_PROGRAM: &str = r" fn main() { @@ -448,12 +500,18 @@ mod tests { } } + impl From for Arguments { + fn from(val: EmptyArguments) -> Self { + val.build_arguments() + } + } + fn dummy_asset_id(byte: u8) -> AssetId { AssetId::from_slice(&[byte; 32]).unwrap() } fn dummy_program() -> Program { - Program::new(DUMMY_PROGRAM, &EmptyArguments) + Program::new(DUMMY_PROGRAM, EmptyArguments {}) } fn dummy_network() -> SimplicityNetwork { diff --git a/crates/sdk/src/program/mod.rs b/crates/sdk/src/program/mod.rs index 8722cf7e..2246c34a 100644 --- a/crates/sdk/src/program/mod.rs +++ b/crates/sdk/src/program/mod.rs @@ -9,8 +9,8 @@ pub mod logger; /// Definitions and traits for resolving and satisfying execution witnesses for Simplicity programs. pub mod witness; -pub use arguments::ArgumentsTrait; -pub use core::{Program, ProgramTrait}; +pub use arguments::{ArgumentsTrait, RandomArguments}; +pub use core::{Program, ProgramFactory, ProgramTrait}; pub use error::ProgramError; pub use simplicityhl::tracker::TrackerLogLevel; -pub use witness::WitnessTrait; +pub use witness::{RandomWitness, WitnessTrait}; diff --git a/crates/sdk/src/program/witness.rs b/crates/sdk/src/program/witness.rs index 7bf47777..eaa451b0 100644 --- a/crates/sdk/src/program/witness.rs +++ b/crates/sdk/src/program/witness.rs @@ -2,10 +2,17 @@ use dyn_clone::DynClone; use simplicityhl::WitnessValues; /// An interface for structs capable of generating Simplicity program witness mappings. -/// See the ` include_simc!()` macro, which generates an automatic `WitnessTrait` implementation. +/// See the ` include_simf!()` macro, which generates an automatic `WitnessTrait` implementation. pub trait WitnessTrait: DynClone { /// Compiles and generates the fully populated `WitnessValues` map for execution. fn build_witness(&self) -> WitnessValues; } dyn_clone::clone_trait_object!(WitnessTrait); + +/// An interface for the struct capable of generating proper `WitnessValues` mappings using the provided RNG. +/// See the ` include_simf!()` macro, which generates an automatic `RandomWitness` implementation. +pub trait RandomWitness: WitnessTrait { + /// Generates a random `WitnessValues` instance using the provided RNG. + fn generate_witness(rng: &mut dyn rand_core::RngCore) -> WitnessValues; +} diff --git a/crates/sdk/src/signer/core.rs b/crates/sdk/src/signer/core.rs index 2fca5996..f302c19b 100644 --- a/crates/sdk/src/signer/core.rs +++ b/crates/sdk/src/signer/core.rs @@ -39,7 +39,7 @@ use crate::program::logger::ProgramLogger; use crate::provider::ProviderTrait; use crate::provider::SimplicityNetwork; use crate::signer::wtns_injector::WtnsInjector; -use crate::transaction::{ChangeOutput, FinalTransaction, PartialOutput, RequiredSignature}; +use crate::transaction::{ChangeOutput, FinalTransaction, PartialOutput, RequiredSignature, SigMessage}; #[cfg(feature = "provider")] use crate::transaction::{PartialInput, TxReceipt, UTXO}; @@ -61,6 +61,7 @@ pub trait SignerTrait { input_index: usize, network: &SimplicityNetwork, derivation_path: Option<&DerivationPath>, + message: &SigMessage, ) -> Result; /// Generates an ECDSA signature to spend a standard transaction input. @@ -98,9 +99,11 @@ impl SignerTrait for Signer { input_index: usize, network: &SimplicityNetwork, derivation_path: Option<&DerivationPath>, + message: &SigMessage, ) -> Result { let env = program.get_env(pst, input_index, network)?; - let msg = Message::from_digest(env.c_tx_env().sighash_all().to_byte_array()); + let sighash = env.c_tx_env().sighash_all().to_byte_array(); + let msg = Message::from_digest(message.digest(sighash)); let private_key = self.get_private_key_at(derivation_path); let keypair = Keypair::from_secret_key(&self.secp, &private_key.inner); @@ -527,7 +530,7 @@ impl Signer { self.network.policy_asset(), )); - let final_tx = self.sign_tx(&fee_tx)?; + let final_tx = self.sign_and_extract_tx(&fee_tx)?; let fee = fee_tx.calculate_fee(final_tx.discount_weight(), fee_rate); if available_delta > fee && available_delta - fee >= MIN_FEE { @@ -537,7 +540,7 @@ impl Signer { outputs[outputs.len() - 2].amount = available_delta - fee; outputs[outputs.len() - 1].amount = fee; - let final_tx = self.sign_tx(&fee_tx)?; + let final_tx = self.sign_and_extract_tx(&fee_tx)?; return Ok(Estimate::Success(final_tx, fee)); } @@ -549,7 +552,7 @@ impl Signer { // Fix this by adding a dummy change or throwing a clear error. fee_tx.remove_output(fee_tx.n_outputs() - 2); - let final_tx = self.sign_tx(&fee_tx)?; + let final_tx = self.sign_and_extract_tx(&fee_tx)?; let fee = fee_tx.calculate_fee(final_tx.discount_weight(), fee_rate); if available_delta < fee { @@ -562,12 +565,19 @@ impl Signer { outputs[outputs.len() - 1].amount = available_delta; // Finalize the tx with fee and without the change - let final_tx = self.sign_tx(&fee_tx)?; + let final_tx = self.sign_and_extract_tx(&fee_tx)?; Ok(Estimate::Success(final_tx, fee)) } - fn sign_tx(&self, tx: &FinalTransaction) -> Result { + /// Signs transaction in raw format for easy processing later in a format of `PartiallySignedTransaction`. + /// + /// # Errors + /// Returns a `SignerError` if we have an error in singing and constructing program witness. + /// + /// # Panics + /// Throws a panic if we failed to sign a program witness. + pub fn sign_tx(&self, tx: &FinalTransaction) -> Result { let (mut pst, secrets) = tx.extract_pst(); let inputs = tx.inputs(); @@ -578,25 +588,31 @@ impl Signer { for (index, input_i) in inputs.iter().enumerate() { // We need to prune the program if let Some(program_input) = &input_i.program_input { - let signing_info: Option<(&String, &[String])> = match &input_i.required_sig { - RequiredSignature::Witness(wtns_name) => Some((wtns_name, &[])), - RequiredSignature::WitnessWithPath(wtns_name, sig_path) => Some((wtns_name, sig_path)), + let signing_info: Option<(&String, &[String], &SigMessage)> = match &input_i.required_sig { + RequiredSignature::Witness(wtns_name) => Some((wtns_name, &[], &SigMessage::Sighash)), + RequiredSignature::WitnessWithPath(wtns_name, sig_path) => { + Some((wtns_name, sig_path, &SigMessage::Sighash)) + } + RequiredSignature::WitnessWithMessage(wtns_name, sig_path, message) => { + Some((wtns_name, sig_path, message)) + } _ => None, }; let signed_witness: Result = match signing_info { // Sign the program and inject the signature into the witness - Some((witness_name, sig_path)) => Ok(self.get_signed_program_witness( + Some((witness_name, sig_path, message)) => Ok(self.get_signed_program_witness( &pst, program_input.program.as_ref(), - &program_input.witness.build_witness(), + &program_input.witness, witness_name, sig_path, index, input_i.partial_input.derivation_path.as_ref(), + message, )?), // Just build the witness - None => Ok(program_input.witness.build_witness()), + None => Ok(program_input.witness.shallow_clone()), }; let pruned_witness = program_input @@ -622,11 +638,21 @@ impl Signer { } } - Ok(pst.extract_tx()?) + Ok(pst) } + fn sign_and_extract_tx(&self, tx: &FinalTransaction) -> Result { + Ok(self.sign_tx(tx)?.extract_tx()?) + } + + /// Signs and inserts a signature into appropriate witness value. + /// + /// # Errors + /// Returns a `SignerError` if signing the program fails, if the witness types cannot be + /// retrieved from the program, if `witness_name` is not present among the program's + /// witness fields, or if injecting the signature into the witness value at `sig_path` fails. #[allow(clippy::too_many_arguments)] - fn get_signed_program_witness( + pub fn get_signed_program_witness( &self, pst: &PartiallySignedTransaction, program: &dyn ProgramTrait, @@ -635,8 +661,9 @@ impl Signer { sig_path: &[String], index: usize, derivation_path: Option<&DerivationPath>, + message: &SigMessage, ) -> Result { - let signature = self.sign_program(pst, program, index, &self.network, derivation_path)?; + let signature = self.sign_program(pst, program, index, &self.network, derivation_path, message)?; // Inject the signature into the wtns name directly if the path is not provided let sig_val = if sig_path.is_empty() { @@ -647,6 +674,7 @@ impl Signer { .get(&WitnessName::from_str_unchecked(witness_name)) .ok_or(SignerError::WtnsFieldNotFound(witness_name.to_string()))?; + #[allow(clippy::missing_panics_doc)] let local_wtns = Arc::new( witness .get(&WitnessName::from_str_unchecked(witness_name)) diff --git a/crates/sdk/src/transaction/final_transaction.rs b/crates/sdk/src/transaction/final_transaction.rs index e8bf1654..07fead1f 100644 --- a/crates/sdk/src/transaction/final_transaction.rs +++ b/crates/sdk/src/transaction/final_transaction.rs @@ -30,7 +30,7 @@ pub struct IssuanceDetails { } /// Represents the final input structure put into a `FinalTransaction` for processing. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FinalInput { /// Holds the base input data required for the operation. pub partial_input: PartialInput, @@ -140,7 +140,7 @@ impl FinalInput { } /// A struct representing a final (but not yet signed) transaction. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FinalTransaction { inputs: Vec, outputs: Vec, @@ -195,7 +195,9 @@ impl FinalTransaction { /// (i.e. if `required_sig` is `RequiredSignature::Witness` or `RequiredSignature::WitnessWithPath`) pub fn add_input(&mut self, partial_input: PartialInput, required_sig: RequiredSignature) { match required_sig { - RequiredSignature::Witness(_) | RequiredSignature::WitnessWithPath(_, _) => { + RequiredSignature::Witness(_) + | RequiredSignature::WitnessWithPath(_, _) + | RequiredSignature::WitnessWithMessage(_, _, _) => { panic!("Requested signature is not NativeEcdsa or None") } _ => {} @@ -234,7 +236,9 @@ impl FinalTransaction { required_sig: RequiredSignature, ) -> IssuanceDetails { match required_sig { - RequiredSignature::Witness(_) | RequiredSignature::WitnessWithPath(_, _) => { + RequiredSignature::Witness(_) + | RequiredSignature::WitnessWithPath(_, _) + | RequiredSignature::WitnessWithMessage(_, _, _) => { panic!("Requested signature is not NativeEcdsa or None") } _ => {} diff --git a/crates/sdk/src/transaction/mod.rs b/crates/sdk/src/transaction/mod.rs index 9b2e019a..944c3f3c 100644 --- a/crates/sdk/src/transaction/mod.rs +++ b/crates/sdk/src/transaction/mod.rs @@ -12,7 +12,7 @@ pub mod utxo; pub use change_output::ChangeOutput; pub use final_transaction::{FinalInput, FinalTransaction, IssuanceDetails}; -pub use partial_input::{PartialInput, ProgramInput, RequiredSignature}; +pub use partial_input::{PartialInput, ProgramInput, RequiredSignature, SigMessage}; pub use partial_output::PartialOutput; pub use tx_receipt::TxReceipt; pub use utxo::UTXO; diff --git a/crates/sdk/src/transaction/partial_input.rs b/crates/sdk/src/transaction/partial_input.rs index 4c05b051..24d938cc 100644 --- a/crates/sdk/src/transaction/partial_input.rs +++ b/crates/sdk/src/transaction/partial_input.rs @@ -1,14 +1,34 @@ +use std::fmt; +use std::fmt::{Debug, Formatter}; +use std::sync::Arc; + use elements_miniscript::bitcoin::bip32::DerivationPath; +use simplicityhl::WitnessValues; use simplicityhl::elements::confidential::{Asset, Value}; use simplicityhl::elements::pset::Input; use simplicityhl::elements::{AssetId, LockTime, OutPoint, Sequence, TxOut, TxOutSecrets, Txid}; +use simplicityhl::simplicity::hashes::Hash; use crate::program::ProgramTrait; -use crate::program::WitnessTrait; +use crate::utils::tagged_hash; use super::UTXO; +/// Derives the 32-byte message to sign from the input's `sighash_all`. +pub type SigMessageFn = Arc [u8; 32] + Send + Sync>; + +/// Defines the 32-byte message a witness signature actually covers. +#[derive(Clone)] +pub enum SigMessage { + /// Sign `sighash_all` itself. + Sighash, + /// Sign the BIP-340 tagged hash `sha256(sha256(tag) || sha256(tag) || sighash_all)`. + Tagged(String), + /// Sign whatever the closure derives from `sighash_all`. + Custom(SigMessageFn), +} + /// Defines the type of signature required for an input. #[derive(Debug, Clone)] pub enum RequiredSignature { @@ -20,6 +40,30 @@ pub enum RequiredSignature { Witness(String), /// A witness payload requiring traversal through a specified path hierarchy. WitnessWithPath(String, Vec), + /// Like `WitnessWithPath`, but over a message derived from `sighash_all`. + WitnessWithMessage(String, Vec, SigMessage), +} + +impl SigMessage { + /// Derives the message to sign from the input's `sighash_all`. + #[must_use] + pub fn digest(&self, sighash: [u8; 32]) -> [u8; 32] { + match self { + Self::Sighash => sighash, + Self::Tagged(tag) => tagged_hash(tag, &sighash).to_byte_array(), + Self::Custom(derive) => derive(sighash), + } + } +} + +impl fmt::Debug for SigMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sighash => f.write_str("Sighash"), + Self::Tagged(tag) => f.debug_tuple("Tagged").field(tag).finish(), + Self::Custom(_) => f.write_str("Custom()"), + } + } } impl RequiredSignature { @@ -34,6 +78,28 @@ impl RequiredSignature { path.into_iter().map(|s| s.as_ref().to_string()).collect(), ) } + + /// Creates a `WitnessWithMessage` requirement using an iterator of path segments. + pub fn witness_with_message(name: &str, path: I, message: SigMessage) -> Self + where + I: IntoIterator, + I::Item: AsRef, + { + RequiredSignature::WitnessWithMessage( + name.to_string(), + path.into_iter().map(|s| s.as_ref().to_string()).collect(), + message, + ) + } + + /// Creates a requirement for a BIP-340 tagged signature over `tag` and `sighash_all`. + pub fn witness_tagged(name: &str, path: I, tag: &str) -> Self + where + I: IntoIterator, + I::Item: AsRef, + { + Self::witness_with_message(name, path, SigMessage::Tagged(tag.to_string())) + } } /// Represents partially prepared input data for Elements transactions. @@ -70,7 +136,13 @@ pub struct ProgramInput { /// The compiled program interface associated with the input. pub program: Box, /// The witness values required to satisfy the program. - pub witness: Box, + pub witness: WitnessValues, +} + +impl Debug for ProgramInput { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", self.witness) + } } /// Represents an input designated for asset issuance or reissuance. @@ -186,8 +258,11 @@ impl PartialInput { impl ProgramInput { /// Creates a new `ProgramInput` from a `ProgramTrait` and its associated `WitnessTrait`. #[must_use] - pub fn new(program: Box, witness: Box) -> Self { - Self { program, witness } + pub fn new(program: Box, witness: impl Into) -> Self { + Self { + program, + witness: witness.into(), + } } } @@ -239,3 +314,54 @@ impl IssuanceInput { } } } + +#[cfg(test)] +mod tests { + use super::*; + + const SIGHASH: [u8; 32] = [0x11; 32]; + + #[test] + fn sighash_message_is_the_sighash() { + assert_eq!(SigMessage::Sighash.digest(SIGHASH), SIGHASH); + } + + #[test] + fn tagged_message_is_the_tagged_hash() { + assert_eq!( + SigMessage::Tagged("SimplexTag".to_string()).digest(SIGHASH), + tagged_hash("SimplexTag", &SIGHASH).to_byte_array() + ); + } + + #[test] + fn custom_message_receives_the_sighash() { + let message = SigMessage::Custom(Arc::new(|sighash: [u8; 32]| { + let mut digest = sighash; + digest[0] ^= 0xff; + + digest + })); + + let mut expected = SIGHASH; + expected[0] ^= 0xff; + + assert_eq!(message.digest(SIGHASH), expected); + } + + #[test] + fn tagged_constructor_builds_a_tagged_message() { + let required = RequiredSignature::witness_tagged("SIGNATURE", ["Left", "1"], "SimplexTag"); + + let RequiredSignature::WitnessWithMessage(name, path, message) = required else { + panic!("expected a WitnessWithMessage requirement"); + }; + + assert_eq!(name, "SIGNATURE"); + assert_eq!(path, ["Left", "1"]); + assert_eq!( + message.digest(SIGHASH), + tagged_hash("SimplexTag", &SIGHASH).to_byte_array() + ); + } +} diff --git a/crates/sdk/src/transaction/utxo.rs b/crates/sdk/src/transaction/utxo.rs index d4423bcd..65444396 100644 --- a/crates/sdk/src/transaction/utxo.rs +++ b/crates/sdk/src/transaction/utxo.rs @@ -11,6 +11,16 @@ pub struct UTXO { pub secrets: Option, } +impl Default for UTXO { + fn default() -> Self { + Self { + outpoint: OutPoint::default(), + txout: TxOut::new_fee(0, AssetId::default()), + secrets: None, + } + } +} + impl UTXO { /// Retrieves the explicit `AssetId` from the transaction output (`txout`). /// diff --git a/crates/sdk/src/utils.rs b/crates/sdk/src/utils.rs index ad596e1e..21c369c4 100644 --- a/crates/sdk/src/utils.rs +++ b/crates/sdk/src/utils.rs @@ -39,10 +39,10 @@ pub fn asset_entropy(outpoint: &OutPoint, entropy: [u8; 32]) -> sha256::Midstate AssetId::generate_asset_entropy(*outpoint, contract_hash) } -/// Hashes arbitrary data with and additional `TapData` and tags. +/// Computes the BIP-340 tagged hash `sha256(sha256(tag) || sha256(tag) || data)`. #[must_use] -pub fn tap_data_hash(data: &[u8]) -> sha256::Hash { - let tag = sha256::Hash::hash(b"TapData"); +pub fn tagged_hash(tag: &str, data: &[u8]) -> sha256::Hash { + let tag = sha256::Hash::hash(tag.as_bytes()); let mut eng = sha256::Hash::engine(); eng.input(tag.as_byte_array()); @@ -52,6 +52,12 @@ pub fn tap_data_hash(data: &[u8]) -> sha256::Hash { sha256::Hash::from_engine(eng) } +/// Hashes arbitrary data with and additional `TapData` and tags. +#[must_use] +pub fn tap_data_hash(data: &[u8]) -> sha256::Hash { + tagged_hash("TapData", data) +} + /// Computes the SHA-256 hash of a given script and returns the resulting 32-byte array. #[must_use] pub fn hash_script(script: &Script) -> [u8; 32] { @@ -81,4 +87,27 @@ mod tests { fn generates_mnemonic() { let _ = random_mnemonic(); } + + #[test] + fn tagged_hash_doubles_the_tag_digest() { + let data = [0x42_u8; 32]; + let tag_digest = Sha256::digest(b"OracleNetworkV1/Price"); + + let mut expected = Sha256::new(); + expected.update(tag_digest); + expected.update(tag_digest); + expected.update(data); + + assert_eq!( + tagged_hash("OracleNetworkV1/Price", &data).to_byte_array(), + <[u8; 32]>::from(expected.finalize()) + ); + } + + #[test] + fn tap_data_hash_is_tagged_with_tap_data() { + let data = b"payload"; + + assert_eq!(tap_data_hash(data), tagged_hash("TapData", data)); + } } diff --git a/crates/simplex/Cargo.toml b/crates/simplex/Cargo.toml index 61abf849..eeb68f67 100644 --- a/crates/simplex/Cargo.toml +++ b/crates/simplex/Cargo.toml @@ -24,6 +24,8 @@ smplx-sdk = { workspace = true, features = ["provider"] } serde = { workspace = true } simplicityhl = { workspace = true, features = ["serde"] } +rand_core = { workspace = true } +rand = { workspace = true } either = { version = "1.15.0", features = ["serde"] } diff --git a/crates/simplex/src/lib.rs b/crates/simplex/src/lib.rs index 4e3525b9..5b7f598a 100644 --- a/crates/simplex/src/lib.rs +++ b/crates/simplex/src/lib.rs @@ -1,4 +1,6 @@ pub use either; +pub use rand; +pub use rand_core; pub use serde; pub use simplicityhl; @@ -6,6 +8,7 @@ pub use smplx_sdk::*; pub use smplx_test::config::TestConfig; pub use smplx_test::context::TestContext; +pub use smplx_test::fuzz; pub use smplx_macros; -pub use smplx_macros::{include_simf, test}; +pub use smplx_macros::{fuzz, include_simf, test}; diff --git a/crates/simplex/tests/ui/array_tr_storage.rs b/crates/simplex/tests/ui/array_tr_storage.rs index 26ad3d9a..112a9149 100644 --- a/crates/simplex/tests/ui/array_tr_storage.rs +++ b/crates/simplex/tests/ui/array_tr_storage.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{ArgumentsTrait, WitnessTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct ArrayTrStorageProgram { + program: Program, +} +impl ArrayTrStorageProgram { + pub const SOURCE: &'static str = derived_array_tr_storage::ARRAY_TR_STORAGE_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for ArrayTrStorageProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for ArrayTrStorageProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/array_tr_storage.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_array_tr_storage::ArrayTrStorageWitness::default(); let witness_values = original_witness.build_witness(); @@ -18,3 +91,88 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_array_tr_storage::ArrayTrStorageWitness::default(), + derived_array_tr_storage::ArrayTrStorageWitness::default() + ); + assert_eq!( + derived_array_tr_storage::ArrayTrStorageArguments::default(), + derived_array_tr_storage::ArrayTrStorageArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_array_tr_storage::ArrayTrStorageWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_array_tr_storage::ArrayTrStorageWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_array_tr_storage::ArrayTrStorageWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_array_tr_storage::ArrayTrStorageArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_array_tr_storage::ArrayTrStorageArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_array_tr_storage::ArrayTrStorageArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_array_tr_storage::ArrayTrStorageWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_array_tr_storage::ArrayTrStorageWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_array_tr_storage::ArrayTrStorageArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_array_tr_storage::ArrayTrStorageArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} \ No newline at end of file diff --git a/crates/simplex/tests/ui/bytes32_tr_storage.rs b/crates/simplex/tests/ui/bytes32_tr_storage.rs index de30a3f4..ba438a7a 100644 --- a/crates/simplex/tests/ui/bytes32_tr_storage.rs +++ b/crates/simplex/tests/ui/bytes32_tr_storage.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct Bytes32TrStorageProgram { + program: Program, +} +impl Bytes32TrStorageProgram { + pub const SOURCE: &'static str = derived_bytes32_tr_storage::BYTES32_TR_STORAGE_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for Bytes32TrStorageProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for Bytes32TrStorageProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/bytes32_tr_storage.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_bytes32_tr_storage::Bytes32TrStorageWitness::default(); let witness_values = original_witness.build_witness(); @@ -18,3 +91,90 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_bytes32_tr_storage::Bytes32TrStorageWitness::default(), + derived_bytes32_tr_storage::Bytes32TrStorageWitness::default() + ); + assert_eq!( + derived_bytes32_tr_storage::Bytes32TrStorageArguments::default(), + derived_bytes32_tr_storage::Bytes32TrStorageArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_bytes32_tr_storage::Bytes32TrStorageWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_bytes32_tr_storage::Bytes32TrStorageWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_bytes32_tr_storage::Bytes32TrStorageWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = + derived_bytes32_tr_storage::Bytes32TrStorageArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = + derived_bytes32_tr_storage::Bytes32TrStorageArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = + derived_bytes32_tr_storage::Bytes32TrStorageArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_bytes32_tr_storage::Bytes32TrStorageWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_bytes32_tr_storage::Bytes32TrStorageWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_bytes32_tr_storage::Bytes32TrStorageArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_bytes32_tr_storage::Bytes32TrStorageArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/dual_currency_deposit.rs b/crates/simplex/tests/ui/dual_currency_deposit.rs index a1395759..d016d1ba 100644 --- a/crates/simplex/tests/ui/dual_currency_deposit.rs +++ b/crates/simplex/tests/ui/dual_currency_deposit.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct DualCurrencyDepositProgram { + program: Program, +} +impl DualCurrencyDepositProgram { + pub const SOURCE: &'static str = derived_dual_currency_deposit::DUAL_CURRENCY_DEPOSIT_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for DualCurrencyDepositProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for DualCurrencyDepositProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/dual_currency_deposit.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_dual_currency_deposit::DualCurrencyDepositWitness::default(); let witness_values = original_witness.build_witness(); @@ -19,3 +92,94 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_dual_currency_deposit::DualCurrencyDepositWitness::default(), + derived_dual_currency_deposit::DualCurrencyDepositWitness::default() + ); + assert_eq!( + derived_dual_currency_deposit::DualCurrencyDepositArguments::default(), + derived_dual_currency_deposit::DualCurrencyDepositArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = + derived_dual_currency_deposit::DualCurrencyDepositWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = + derived_dual_currency_deposit::DualCurrencyDepositWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = + derived_dual_currency_deposit::DualCurrencyDepositWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = + derived_dual_currency_deposit::DualCurrencyDepositArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = + derived_dual_currency_deposit::DualCurrencyDepositArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = + derived_dual_currency_deposit::DualCurrencyDepositArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_dual_currency_deposit::DualCurrencyDepositWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_dual_currency_deposit::DualCurrencyDepositWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = + derived_dual_currency_deposit::DualCurrencyDepositArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_dual_currency_deposit::DualCurrencyDepositArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/either_with_single_witness.rs b/crates/simplex/tests/ui/either_with_single_witness.rs index 9f1186ef..5755d79d 100644 --- a/crates/simplex/tests/ui/either_with_single_witness.rs +++ b/crates/simplex/tests/ui/either_with_single_witness.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct EitherWithSingleWitnessProgram { + program: Program, +} +impl EitherWithSingleWitnessProgram { + pub const SOURCE: &'static str = derived_either_with_single_witness::EITHER_WITH_SINGLE_WITNESS_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for EitherWithSingleWitnessProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for EitherWithSingleWitnessProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/either_with_single_witness.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_either_with_single_witness::EitherWithSingleWitnessWitness::default(); let witness_values = original_witness.build_witness(); @@ -20,3 +93,97 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_either_with_single_witness::EitherWithSingleWitnessWitness::default(), + derived_either_with_single_witness::EitherWithSingleWitnessWitness::default() + ); + assert_eq!( + derived_either_with_single_witness::EitherWithSingleWitnessArguments::default(), + derived_either_with_single_witness::EitherWithSingleWitnessArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = + derived_either_with_single_witness::EitherWithSingleWitnessWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = + derived_either_with_single_witness::EitherWithSingleWitnessWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = + derived_either_with_single_witness::EitherWithSingleWitnessWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = + derived_either_with_single_witness::EitherWithSingleWitnessArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = + derived_either_with_single_witness::EitherWithSingleWitnessArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = + derived_either_with_single_witness::EitherWithSingleWitnessArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = + derived_either_with_single_witness::EitherWithSingleWitnessWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_either_with_single_witness::EitherWithSingleWitnessWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = + derived_either_with_single_witness::EitherWithSingleWitnessArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_either_with_single_witness::EitherWithSingleWitnessArguments::from_arguments( + &arguments_values, + )?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/exotic_values.rs b/crates/simplex/tests/ui/exotic_values.rs index e0aab6a7..03ddf9e2 100644 --- a/crates/simplex/tests/ui/exotic_values.rs +++ b/crates/simplex/tests/ui/exotic_values.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct ExoticValuesProgram { + program: Program, +} +impl ExoticValuesProgram { + pub const SOURCE: &'static str = derived_exotic_values::EXOTIC_VALUES_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for ExoticValuesProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for ExoticValuesProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/exotic_values.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_exotic_values::ExoticValuesWitness::default(); let witness_values = original_witness.build_witness(); @@ -13,9 +86,92 @@ fn main() -> Result<(), String> { let original_arguments = derived_exotic_values::ExoticValuesArguments::default(); let arguments_values = original_arguments.build_arguments(); - let recovered_arguments = - derived_exotic_values::ExoticValuesArguments::from_arguments(&arguments_values)?; + let recovered_arguments = derived_exotic_values::ExoticValuesArguments::from_arguments(&arguments_values)?; assert_eq!(original_arguments, recovered_arguments); Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_exotic_values::ExoticValuesWitness::default(), + derived_exotic_values::ExoticValuesWitness::default() + ); + assert_eq!( + derived_exotic_values::ExoticValuesArguments::default(), + derived_exotic_values::ExoticValuesArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_exotic_values::ExoticValuesWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_exotic_values::ExoticValuesWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_exotic_values::ExoticValuesWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_exotic_values::ExoticValuesArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_exotic_values::ExoticValuesArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_exotic_values::ExoticValuesArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_exotic_values::ExoticValuesWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_exotic_values::ExoticValuesWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_exotic_values::ExoticValuesArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_exotic_values::ExoticValuesArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/list_check.rs b/crates/simplex/tests/ui/list_check.rs index a36079a0..155a368e 100644 --- a/crates/simplex/tests/ui/list_check.rs +++ b/crates/simplex/tests/ui/list_check.rs @@ -1,9 +1,84 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct ListCheckProgram { + program: Program, +} +impl ListCheckProgram { + pub const SOURCE: &'static str = derived_list_check::LIST_CHECK_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for ListCheckProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for ListCheckProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/list_check.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_build_panic()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_list_check::ListCheckWitness { draft: vec![], path: simplex::either::Left(vec![ @@ -17,12 +92,16 @@ fn main() -> Result<(), String> { let recovered_witness = derived_list_check::ListCheckWitness::from_witness(&witness_values)?; assert_eq!(original_witness, recovered_witness); - let original_arguments = derived_list_check::ListCheckArguments {}; + let original_arguments = derived_list_check::ListCheckArguments::default(); let arguments_values = original_arguments.build_arguments(); let recovered_arguments = derived_list_check::ListCheckArguments::from_arguments(&arguments_values)?; assert_eq!(original_arguments, recovered_arguments); + Ok(()) +} + +fn test_build_panic() -> Result<(), String> { // Build Witness, which would panic on building let original_witness = derived_list_check::ListCheckWitness { draft: vec![], @@ -37,13 +116,99 @@ fn main() -> Result<(), String> { // Register panic hook to reduce warnings let default_hook = std::panic::take_hook(); std::panic::set_hook(Box::new(|_| {})); - let result = std::panic::catch_unwind(|| { - original_witness.build_witness() - }); + let result = std::panic::catch_unwind(|| original_witness.build_witness()); std::panic::set_hook(default_hook); - assert!(result.is_err(), "Expected build_witness to panic, as we have Vec size equal to list size, but it succeeded."); + assert!( + result.is_err(), + "Expected build_witness to panic, as we have Vec size equal to list size, but it succeeded." + ); + + Ok(()) +} + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_list_check::ListCheckWitness::default(), + derived_list_check::ListCheckWitness::default() + ); + assert_eq!( + derived_list_check::ListCheckArguments::default(), + derived_list_check::ListCheckArguments::default() + ); + + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_list_check::ListCheckWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_list_check::ListCheckWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_list_check::ListCheckWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_list_check::ListCheckArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_list_check::ListCheckArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_list_check::ListCheckArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_list_check::ListCheckWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_list_check::ListCheckWitness::from_witness(&witness_values)?; + } + { + let arguments_values = derived_list_check::ListCheckArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_list_check::ListCheckArguments::from_arguments(&arguments_values)?; + } + } Ok(()) } +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/option_offer.rs b/crates/simplex/tests/ui/option_offer.rs index 3ace0aeb..f1a10cbc 100644 --- a/crates/simplex/tests/ui/option_offer.rs +++ b/crates/simplex/tests/ui/option_offer.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct OptionOfferProgram { + program: Program, +} +impl OptionOfferProgram { + pub const SOURCE: &'static str = derived_option_offer::OPTION_OFFER_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for OptionOfferProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for OptionOfferProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/option_offer.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_option_offer::OptionOfferWitness::default(); let witness_values = original_witness.build_witness(); @@ -18,3 +91,87 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_option_offer::OptionOfferWitness::default(), + derived_option_offer::OptionOfferWitness::default() + ); + assert_eq!( + derived_option_offer::OptionOfferArguments::default(), + derived_option_offer::OptionOfferArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_option_offer::OptionOfferWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_option_offer::OptionOfferWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_option_offer::OptionOfferWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_option_offer::OptionOfferArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_option_offer::OptionOfferArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_option_offer::OptionOfferArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_option_offer::OptionOfferWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_option_offer::OptionOfferWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_option_offer::OptionOfferArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_option_offer::OptionOfferArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/options.rs b/crates/simplex/tests/ui/options.rs index 9dad90af..d2b8d48f 100644 --- a/crates/simplex/tests/ui/options.rs +++ b/crates/simplex/tests/ui/options.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct OptionsProgram { + program: Program, +} +impl OptionsProgram { + pub const SOURCE: &'static str = derived_options::OPTIONS_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for OptionsProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for OptionsProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/options.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_options::OptionsWitness::default(); let witness_values = original_witness.build_witness(); @@ -18,3 +91,87 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_options::OptionsWitness::default(), + derived_options::OptionsWitness::default() + ); + assert_eq!( + derived_options::OptionsArguments::default(), + derived_options::OptionsArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_options::OptionsWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_options::OptionsWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_options::OptionsWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_options::OptionsArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_options::OptionsArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_options::OptionsArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_options::OptionsWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_options::OptionsWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_options::OptionsArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_options::OptionsArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/simple_storage.rs b/crates/simplex/tests/ui/simple_storage.rs index 34147700..58d1d5b8 100644 --- a/crates/simplex/tests/ui/simple_storage.rs +++ b/crates/simplex/tests/ui/simple_storage.rs @@ -1,9 +1,82 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct SimpleStorageProgram { + program: Program, +} +impl SimpleStorageProgram { + pub const SOURCE: &'static str = derived_simple_storage::SIMPLE_STORAGE_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for SimpleStorageProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for SimpleStorageProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/simple_storage.simf"); fn main() -> Result<(), String> { + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; + + Ok(()) +} + +fn test_e2e_behaviour() -> Result<(), String> { let original_witness = derived_simple_storage::SimpleStorageWitness::default(); let witness_values = original_witness.build_witness(); @@ -18,3 +91,87 @@ fn main() -> Result<(), String> { Ok(()) } + +fn test_default() -> Result<(), String> { + assert_eq!( + derived_simple_storage::SimpleStorageWitness::default(), + derived_simple_storage::SimpleStorageWitness::default() + ); + assert_eq!( + derived_simple_storage::SimpleStorageArguments::default(), + derived_simple_storage::SimpleStorageArguments::default() + ); + Ok(()) +} + +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + let original_witness = derived_simple_storage::SimpleStorageWitness::generate_witness_raw(&mut rng); + + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_simple_storage::SimpleStorageWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_simple_storage::SimpleStorageWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); + + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_simple_storage::SimpleStorageArguments::generate_arguments_raw(&mut rng); + + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_simple_storage::SimpleStorageArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_simple_storage::SimpleStorageArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } + Ok(()) +} + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_simple_storage::SimpleStorageWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_simple_storage::SimpleStorageWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_simple_storage::SimpleStorageArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_simple_storage::SimpleStorageArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/simplex/tests/ui/single_bit.rs b/crates/simplex/tests/ui/single_bit.rs index d1397cc2..153c360d 100644 --- a/crates/simplex/tests/ui/single_bit.rs +++ b/crates/simplex/tests/ui/single_bit.rs @@ -1,84 +1,179 @@ use simplex::include_simf; -use simplex::program::{WitnessTrait, ArgumentsTrait}; +use simplex::fuzz::{generate_value_by_ty}; +use simplex::program::Program; +use simplex::program::{ArgumentsTrait, RandomArguments, RandomWitness, WitnessTrait}; +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::secp256k1_zkp::XOnlyPublicKey; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::{Arguments, WitnessValues}; +use simplex::rand::RngCore; + +#[derive(Clone)] +pub struct SingleBitProgram { + program: Program, +} +impl SingleBitProgram { + pub const SOURCE: &'static str = derived_single_bit::SINGLE_BIT_CONTRACT_SOURCE; + #[must_use] + pub fn new(arguments: impl Into) -> Self { + Self { + program: Program::new(Self::SOURCE, arguments.into()), + } + } + #[must_use] + pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self { + self.program = self.program.with_taproot_pubkey(pub_key); + self + } + #[must_use] + pub fn with_storage_capacity(mut self, capacity: usize) -> Self { + self.program = self.program.with_storage_capacity(capacity); + self + } + pub fn set_storage_at(&mut self, index: usize, new_value: impl Into>) { + self.program.set_storage_at(index, new_value); + } + #[must_use] + pub fn get_storage_len(&self) -> usize { + self.program.get_storage_len() + } + #[must_use] + pub fn get_storage(&self) -> &[Vec] { + self.program.get_storage() + } + #[must_use] + pub fn get_storage_at(&self, index: usize) -> Vec { + self.program.get_storage_at(index) + } + #[must_use] + pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script { + self.program.get_script_pubkey(network) + } + #[must_use] + pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] { + self.program.get_script_hash(network) + } +} +impl AsRef for SingleBitProgram { + fn as_ref(&self) -> &Program { + &self.program + } +} +impl AsMut for SingleBitProgram { + fn as_mut(&mut self) -> &mut Program { + &mut self.program + } +} include_simf!("../../../../crates/simplex/tests/ui_simfs/single_bit.simf"); fn main() -> Result<(), String> { - // Testing values with (FLAG = 1, BIT = 1) - let original_witness = derived_single_bit::SingleBitWitness { bit: 1 }; + let _ = test_e2e_behaviour()?; + let _ = test_default()?; + let _ = test_e2e_random_behaviour()?; + let _ = test_e2e_random_value_generation_behaviour()?; - let witness_values = original_witness.build_witness(); - let recovered_witness = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; - assert_eq!( - original_witness, recovered_witness, - "Testing values with (FLAG = 1, BIT = 1) (Witness)" - ); + Ok(()) +} - let original_arguments = derived_single_bit::SingleBitArguments { flag: 1 }; +fn test_e2e_behaviour() -> Result<(), String> { + for (bit, flag) in [(1, 1), (1, 0), (0, 1), (0, 0)] { + let original_witness = derived_single_bit::SingleBitWitness { bit }; - let arguments_values = original_arguments.build_arguments(); - let recovered_arguments = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; - assert_eq!( - original_arguments, recovered_arguments, - "Testing values with (FLAG = 1, BIT = 1) (Arguments)" - ); + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); - // Testing values with (FLAG = 0, BIT = 1) - let original_witness = derived_single_bit::SingleBitWitness { bit: 1 }; + let original_arguments = derived_single_bit::SingleBitArguments { flag }; - let witness_values = original_witness.build_witness(); - let recovered_witness = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; - assert_eq!( - original_witness, recovered_witness, - "Testing values with (FLAG = 0, BIT = 1) (Witness)" - ); + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + } - let original_arguments = derived_single_bit::SingleBitArguments { flag: 0 }; + Ok(()) +} - let arguments_values = original_arguments.build_arguments(); - let recovered_arguments = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; +fn test_default() -> Result<(), String> { assert_eq!( - original_arguments, recovered_arguments, - "Testing values with (FLAG = 0, BIT = 1) (Arguments)" + derived_single_bit::SingleBitWitness::default(), + derived_single_bit::SingleBitWitness::default() ); - - // Testing values with (FLAG = 1, BIT = 0) - let original_witness = derived_single_bit::SingleBitWitness { bit: 0 }; - - let witness_values = original_witness.build_witness(); - let recovered_witness = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; assert_eq!( - original_witness, recovered_witness, - "Testing values with (FLAG = 1, BIT = 0) (Witness)" + derived_single_bit::SingleBitArguments::default(), + derived_single_bit::SingleBitArguments::default() ); + Ok(()) +} - let original_arguments = derived_single_bit::SingleBitArguments { flag: 1 }; +fn test_e2e_random_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; - let arguments_values = original_arguments.build_arguments(); - let recovered_arguments = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; - assert_eq!( - original_arguments, recovered_arguments, - "Testing values with (FLAG = 1, BIT = 0) (Arguments)" - ); + let mut rng = StdRng::seed_from_u64(seed); - // Testing values with (FLAG = 0, BIT = 0) - let original_witness = derived_single_bit::SingleBitWitness { bit: 0 }; + let original_witness = derived_single_bit::SingleBitWitness::generate_witness_raw(&mut rng); - let witness_values = original_witness.build_witness(); - let recovered_witness = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; - assert_eq!( - original_witness, recovered_witness, - "Testing values with (FLAG = 0, BIT = 0) (Witness)" - ); + let witness_values = original_witness.build_witness(); + let recovered_witness = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; + assert_eq!(original_witness, recovered_witness); - let original_arguments = derived_single_bit::SingleBitArguments { flag: 0 }; + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_single_bit::SingleBitWitness::generate_witness(&mut rng); + assert_eq!(witness_values, rand_raw_witness_values); - let arguments_values = original_arguments.build_arguments(); - let recovered_arguments = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; - assert_eq!( - original_arguments, recovered_arguments, - "Testing values with (FLAG = 0, BIT = 0) (Arguments)" - ); + rng = StdRng::seed_from_u64(seed); + let original_arguments = derived_single_bit::SingleBitArguments::generate_arguments_raw(&mut rng); + let arguments_values = original_arguments.build_arguments(); + let recovered_arguments = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; + assert_eq!(original_arguments, recovered_arguments); + + rng = StdRng::seed_from_u64(seed); + let rand_raw_witness_values = derived_single_bit::SingleBitArguments::generate_arguments(&mut rng); + assert_eq!(arguments_values, rand_raw_witness_values); + } Ok(()) } + +fn test_e2e_random_value_generation_behaviour() -> Result<(), String> { + for seed in 0..32 { + use simplex::rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::seed_from_u64(seed); + + { + let witness_values = derived_single_bit::SingleBitWitness::generate_witness(&mut rng); + + let witness_values = regenerate_witness_values(&witness_values, &mut rng); + let _ = derived_single_bit::SingleBitWitness::from_witness(&witness_values)?; + } + + { + let arguments_values = derived_single_bit::SingleBitArguments::generate_arguments(&mut rng); + + let arguments_values = regenerate_arguments_values(&arguments_values, &mut rng); + let _ = derived_single_bit::SingleBitArguments::from_arguments(&arguments_values)?; + } + } + Ok(()) +} + +fn regenerate_witness_values(wit: &WitnessValues, rng: &mut dyn RngCore) -> WitnessValues { + use std::collections::HashMap; + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in wit.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + WitnessValues::from(map) +} + +fn regenerate_arguments_values(args: &Arguments, rng: &mut dyn RngCore) -> Arguments { + use std::collections::HashMap; + + let mut map: HashMap<_,_> = Default::default(); + for (name, val) in args.iter() { + map.insert(name.clone(), generate_value_by_ty(val.ty(), rng)); + } + Arguments::from(map) +} diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index 6beff889..00fd54f1 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -19,7 +19,10 @@ simplicityhl = { workspace = true } electrsd = { workspace = true } serde = { workspace = true } toml = { workspace = true } +rand = { workspace = true } syn = { version = "2.0.114", default-features = false, features = ["proc-macro", "full", "parsing", "derive", "clone-impls", "extra-traits", "printing"] } proc-macro2 = { version = "1.0.106", features = ["span-locations"] } quote = { version = "1.0.44" } +proptest = { version = "1.11.0", features = ["std"] } + diff --git a/crates/test/src/config.rs b/crates/test/src/config.rs index bacb551d..ca336821 100644 --- a/crates/test/src/config.rs +++ b/crates/test/src/config.rs @@ -22,6 +22,7 @@ pub struct TestConfig { pub bitcoins: u64, pub esplora: Option, pub rpc: Option, + pub proptest: Option, pub verbosity: Verbosity, } @@ -38,6 +39,14 @@ pub struct RpcConfig { pub password: String, } +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct ProptestConfig { + pub cases: Option, + pub max_shrink_iters: Option, + pub max_global_rejects: Option, + pub max_local_rejects: Option, +} + impl TestConfig { pub fn from_file(path: impl AsRef) -> Result { let mut content = String::new(); @@ -80,6 +89,7 @@ impl Default for TestConfig { bitcoins: DEFAULT_BITCOINS, esplora: None, rpc: None, + proptest: None, verbosity: Verbosity::None, } } diff --git a/crates/test/src/error.rs b/crates/test/src/error.rs index dcb52145..1c999cd7 100644 --- a/crates/test/src/error.rs +++ b/crates/test/src/error.rs @@ -4,6 +4,8 @@ use smplx_sdk::provider::ProviderError; use smplx_regtest::error::RegtestError; +use crate::fuzz::builders::ProgramTarget; + #[derive(thiserror::Error, Debug)] pub enum TestError { #[error(transparent)] @@ -33,3 +35,21 @@ pub enum NetworkUtilsError { #[error("Unsuccessful action completion, err: '{0}'")] UnsuccessfulSync(String), } + +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +pub enum FuzzError { + #[error("At least one program target is required")] + NoProgramTargets, + + #[error("Duplicate program target: {0:?}")] + DuplicateProgramTarget(ProgramTarget), + + #[error("Program input target index {index} is out of bounds for {input_count} inputs")] + InputTargetOutOfBounds { index: usize, input_count: usize }, + + #[error("Program output target index {index} is out of bounds for {output_count} outputs")] + OutputTargetOutOfBounds { index: usize, output_count: usize }, + + #[error("Fuzz transaction post hook failed: {0}")] + PostHook(String), +} diff --git a/crates/test/src/fuzz/args_strategy/mod.rs b/crates/test/src/fuzz/args_strategy/mod.rs new file mode 100644 index 00000000..ae56c221 --- /dev/null +++ b/crates/test/src/fuzz/args_strategy/mod.rs @@ -0,0 +1,7 @@ +mod random; +mod random_interesting; +mod random_pool; + +pub use random::*; +pub use random_interesting::*; +pub use random_pool::*; diff --git a/crates/test/src/fuzz/args_strategy/random.rs b/crates/test/src/fuzz/args_strategy/random.rs new file mode 100644 index 00000000..0b59cec0 --- /dev/null +++ b/crates/test/src/fuzz/args_strategy/random.rs @@ -0,0 +1,55 @@ +use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; + +use simplicityhl::{Arguments, WitnessValues}; + +use proptest::prelude::Strategy; +use proptest::strategy::{NewTree, ValueTree}; +use proptest::test_runner::TestRunner; + +use smplx_sdk::program::{RandomArguments, RandomWitness}; + +pub struct Random { + phantom_data: PhantomData<(Args, Wit)>, +} + +impl Default for Random { + fn default() -> Self { + Self { + phantom_data: PhantomData, + } + } +} + +impl Debug for Random { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Random trees...") + } +} + +pub struct RandomValueTree(T); + +impl ValueTree for RandomValueTree { + type Value = T; + fn current(&self) -> T { + self.0.clone() + } + fn simplify(&mut self) -> bool { + false + } + fn complicate(&mut self) -> bool { + false + } +} + +impl Strategy for Random { + type Tree = RandomValueTree<(Arguments, WitnessValues)>; + type Value = (Arguments, WitnessValues); + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + Ok(RandomValueTree(( + Args::generate_arguments(runner.rng()), + Wit::generate_witness(runner.rng()), + ))) + } +} diff --git a/crates/test/src/fuzz/args_strategy/random_interesting.rs b/crates/test/src/fuzz/args_strategy/random_interesting.rs new file mode 100644 index 00000000..908087a9 --- /dev/null +++ b/crates/test/src/fuzz/args_strategy/random_interesting.rs @@ -0,0 +1,288 @@ +use std::collections::HashMap; +use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; + +use simplicityhl::num::U256; +use simplicityhl::types::{TypeInner, UIntType}; +use simplicityhl::value::ValueConstructible; +use simplicityhl::{Arguments, ResolvedType, Value, WitnessValues}; + +use proptest::prelude::Rng; +use proptest::prelude::Strategy; +use proptest::strategy::{NewTree, ValueTree}; +use proptest::test_runner::{TestRng, TestRunner}; +use rand::prelude::IndexedRandom; + +use smplx_sdk::program::{RandomArguments, RandomWitness}; + +static INTERESTING_U4: &[u8] = &[0, 1, 2, 7, 8, 14, 15]; + +static INTERESTING_U8: &[u8] = &[0, 1, 2, 16, 32, 64, 100, (1 << 7) - 1, 1 << 7, u8::MAX - 1, u8::MAX]; + +static INTERESTING_U16: &[u16] = &[ + 0, + 1, + 2, + 16, + 32, + 64, + 100, + 1000, + 1024, + 4096, + (1 << 7) - 1, + 1 << 7, + u8::MAX as u16, + 1 << 8, + (1 << 15) - 1, + 1 << 15, + u16::MAX - 1, + u16::MAX, +]; + +static INTERESTING_U32: &[u32] = &[ + 0, + 1, + 2, + 16, + 32, + 64, + 100, + 1000, + 1024, + 4096, + (1 << 7) - 1, + 1 << 7, + u8::MAX as u32, + 1 << 8, + (1 << 15) - 1, + 1 << 15, + u16::MAX as u32, + 1 << 16, + (1 << 31) - 1, + 1 << 31, + u32::MAX - 1, + u32::MAX, +]; + +static INTERESTING_U64: &[u64] = &[ + 0, + 1, + 2, + 16, + 32, + 64, + 100, + 1000, + 1024, + 4096, + (1 << 7) - 1, + 1 << 7, + u8::MAX as u64, + 1 << 8, + (1 << 15) - 1, + 1 << 15, + u16::MAX as u64, + 1 << 16, + (1 << 31) - 1, + 1 << 31, + u32::MAX as u64, + 1 << 32, + (1 << 63) - 1, + 1 << 63, + u64::MAX - 1, + u64::MAX, +]; + +static INTERESTING_U128: &[u128] = &[ + 0, + 1, + 2, + 16, + 32, + 64, + 100, + 1000, + 1024, + 4096, + (1 << 7) - 1, + 1 << 7, + u8::MAX as u128, + 1 << 8, + (1 << 15) - 1, + 1 << 15, + u16::MAX as u128, + 1 << 16, + (1 << 31) - 1, + 1 << 31, + u32::MAX as u128, + 1 << 32, + (1 << 63) - 1, + 1 << 63, + u64::MAX as u128, + 1 << 64, + (1_u128 << 127) - 1, + 1_u128 << 127, + u128::MAX - 1, + u128::MAX, +]; + +pub struct InterestingRandom { + phantom_data: PhantomData<(Args, Wit)>, +} + +impl Default for InterestingRandom { + fn default() -> Self { + Self { + phantom_data: PhantomData, + } + } +} + +impl Debug for InterestingRandom { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "InterestingRandom trees...") + } +} + +pub struct InterestingRandomValueTree(T); + +impl ValueTree for InterestingRandomValueTree { + type Value = T; + fn current(&self) -> T { + self.0.clone() + } + fn simplify(&mut self) -> bool { + false + } + fn complicate(&mut self) -> bool { + false + } +} + +impl Strategy + for InterestingRandom +{ + type Tree = InterestingRandomValueTree<(Arguments, WitnessValues)>; + type Value = (Arguments, WitnessValues); + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + let args = Args::generate_arguments(runner.rng()); + let wit = Wit::generate_witness(runner.rng()); + + let mut args_map = HashMap::new(); + for (name, val) in args.iter() { + args_map.insert( + name.clone(), + generate_interesting_or_scratch_by_ty(val.ty(), runner.rng()), + ); + } + + let mut wit_map = HashMap::new(); + for (name, val) in wit.iter() { + wit_map.insert( + name.clone(), + generate_interesting_or_scratch_by_ty(val.ty(), runner.rng()), + ); + } + + Ok(InterestingRandomValueTree(( + Arguments::from(args_map), + WitnessValues::from(wit_map), + ))) + } +} + +pub fn generate_interesting_or_scratch_by_ty(ty: &ResolvedType, rng: &mut TestRng) -> Value { + match ty.as_inner() { + TypeInner::Either(left_ty, right_ty) => { + let left_v: bool = rng.random(); + match left_v { + true => Value::left( + generate_interesting_or_scratch_by_ty(left_ty, rng), + (**right_ty).clone(), + ), + false => Value::right( + (**left_ty).clone(), + generate_interesting_or_scratch_by_ty(right_ty, rng), + ), + } + } + TypeInner::Option(option_ty) => { + let is_some: bool = rng.random(); + match is_some { + true => Value::some(generate_interesting_or_scratch_by_ty(option_ty, rng)), + false => Value::none((**option_ty).clone()), + } + } + TypeInner::Boolean => Value::from(rng.random::()), + TypeInner::UInt(x) => { + let use_interesting: bool = rng.random(); + if use_interesting { + match x { + UIntType::U1 => Value::u1(rng.random::() & 0x01), + UIntType::U2 => Value::u2(rng.random::() & 0x03), + UIntType::U4 => { + let val = *INTERESTING_U4.choose(rng).unwrap(); + Value::u4(val) + } + UIntType::U8 => { + let val = *INTERESTING_U8.choose(rng).unwrap(); + Value::u8(val) + } + UIntType::U16 => { + let val = *INTERESTING_U16.choose(rng).unwrap(); + Value::u16(val) + } + UIntType::U32 => { + let val = *INTERESTING_U32.choose(rng).unwrap(); + Value::u32(val) + } + UIntType::U64 => { + let val = *INTERESTING_U64.choose(rng).unwrap(); + Value::u64(val) + } + UIntType::U128 => { + let val = *INTERESTING_U128.choose(rng).unwrap(); + Value::u128(val) + } + UIntType::U256 => { + let val = *INTERESTING_U128.choose(rng).unwrap(); + let mut bytes = [0u8; 32]; + let val_bytes = val.to_be_bytes(); + bytes[16..32].copy_from_slice(&val_bytes); + Value::u256(U256::from_byte_array(bytes)) + } + } + } else { + match x { + UIntType::U1 => Value::u1(rng.random::() & 0x0001), + UIntType::U2 => Value::u2(rng.random::() & 0x0003), + UIntType::U4 => Value::u4(rng.random::() & 0x000F), + UIntType::U8 => Value::u8(rng.random::()), + UIntType::U16 => Value::u16(rng.random::()), + UIntType::U32 => Value::u32(rng.random::()), + UIntType::U64 => Value::u64(rng.random::()), + UIntType::U128 => Value::u128(rng.random::()), + UIntType::U256 => Value::u256(U256::from_byte_array(rng.random())), + } + } + } + TypeInner::Tuple(tuple_ty) => { + Value::tuple(tuple_ty.iter().map(|x| generate_interesting_or_scratch_by_ty(x, rng))) + } + TypeInner::Array(array_ty, size) => Value::array( + (0..*size).map(|_| generate_interesting_or_scratch_by_ty(array_ty, rng)), + (**array_ty).clone(), + ), + TypeInner::List(list_ty, size_pow_2) => { + let size = rng.random_range(0..size_pow_2.get()); + Value::list( + (0..size).map(|_| generate_interesting_or_scratch_by_ty(list_ty, rng)), + (**list_ty).clone(), + *size_pow_2, + ) + } + _ => Value::unit(), + } +} diff --git a/crates/test/src/fuzz/args_strategy/random_pool.rs b/crates/test/src/fuzz/args_strategy/random_pool.rs new file mode 100644 index 00000000..42281f9a --- /dev/null +++ b/crates/test/src/fuzz/args_strategy/random_pool.rs @@ -0,0 +1,154 @@ +use std::collections::HashMap; +use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; + +use simplicityhl::str::WitnessName; +use simplicityhl::{Arguments, ResolvedType, Value, WitnessValues}; + +use proptest::prelude::Rng; +use proptest::prelude::Strategy; +use proptest::strategy::{NewTree, ValueTree}; +use proptest::test_runner::{TestRng, TestRunner}; + +use smplx_sdk::program::{RandomArguments, RandomWitness}; + +pub struct RandomValuePool { + phantom_data: PhantomData<(Args, Wit)>, + _value_pool: ValuePool, +} + +impl Default for RandomValuePool { + fn default() -> Self { + Self { + phantom_data: PhantomData, + _value_pool: ValuePool::default(), + } + } +} + +impl Debug for RandomValuePool { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "RandomValuePool trees...") + } +} + +pub struct ValuePoolValueTree { + current: T, + value_pool: ValuePool, + rng: TestRng, + cnt: usize, + max_bound: usize, +} + +impl ValuePoolValueTree { + pub fn check_utilization(&self) -> bool { + self.cnt < self.max_bound + } +} + +impl ValueTree for ValuePoolValueTree<(Arguments, WitnessValues)> { + type Value = (Arguments, WitnessValues); + fn current(&self) -> Self::Value { + self.current.clone() + } + fn simplify(&mut self) -> bool { + let modified_witness = self + .value_pool + .probabilistically_replace(self.current.1.clone(), &mut self.rng); + self.current.1 = modified_witness; + self.cnt += 1; + self.check_utilization() + } + fn complicate(&mut self) -> bool { + self.simplify() + } +} + +impl Strategy + for RandomValuePool +{ + type Tree = ValuePoolValueTree<(Arguments, WitnessValues)>; + type Value = (Arguments, WitnessValues); + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + let args = Args::generate_arguments(runner.rng()); + let wit = Wit::generate_witness(runner.rng()); + let pool = ValuePool::new(&wit.clone(), &args.clone()); + let wit = pool.probabilistically_replace(wit, runner.rng()); + + Ok(ValuePoolValueTree { + current: (args, wit), + value_pool: pool, + rng: runner.rng().clone(), + cnt: 0, + max_bound: 50, + }) + } +} + +#[derive(Default)] +pub struct ValuePool { + pool: HashMap>, + witness_structure: HashMap, +} + +impl ValuePool { + pub fn new(wit: &WitnessValues, args: &Arguments) -> Self { + let mut pool: HashMap> = HashMap::new(); + let mut witness_structure: HashMap = HashMap::new(); + + for (name, val) in wit.iter() { + witness_structure.insert(name.clone(), val.ty().clone()); + pool.entry(val.ty().clone()) + .and_modify(|counter| counter.push(val.clone())) + .or_insert(vec![val.clone()]); + } + + for (_, val) in args.iter() { + pool.entry(val.ty().clone()) + .and_modify(|counter| counter.push(val.clone())) + .or_insert(vec![val.clone()]); + } + // TODO: add possibility in simplex to generate any kind of type + + Self { + pool, + witness_structure, + } + } + + pub fn sample(&self, ty: &ResolvedType, rng: &mut TestRng) -> Option { + self.pool.get(ty).and_then(|values| { + if values.is_empty() { + None + } else { + let idx = rng.random_range(0..values.len()); + Some(values[idx].clone()) + } + }) + } + + pub fn generate_witness(&self, rng: &mut TestRng) -> WitnessValues { + let mut map = HashMap::new(); + for (name, ty) in &self.witness_structure { + if let Some(val) = self.sample(ty, rng) { + map.insert(name.clone(), val); + } + } + WitnessValues::from(map) + } + + pub fn probabilistically_replace(&self, wit: WitnessValues, rng: &mut TestRng) -> WitnessValues { + let mut map = HashMap::new(); + for (name, val) in wit.iter() { + let should_replace: bool = rng.random(); + if should_replace { + let sampled = self.sample(val.ty(), rng).unwrap_or_else(|| val.clone()); + map.insert(name.clone(), sampled); + } else { + map.insert(name.clone(), val.clone()); + } + } + WitnessValues::from(map) + } +} diff --git a/crates/test/src/fuzz/builders/final_tx_builder.rs b/crates/test/src/fuzz/builders/final_tx_builder.rs new file mode 100644 index 00000000..6ec3bc21 --- /dev/null +++ b/crates/test/src/fuzz/builders/final_tx_builder.rs @@ -0,0 +1,274 @@ +use std::collections::HashSet; + +use simplicityhl::elements::Script; +use simplicityhl::{Arguments, WitnessValues}; + +use smplx_sdk::program::Program; +use smplx_sdk::transaction::{FinalTransaction, ProgramInput}; + +use crate::error::FuzzError; + +type PostHook = dyn Fn(&mut FinalTransaction, &Script, &Arguments, &WitnessValues) -> Result<(), FuzzError>; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum ProgramTarget { + Input(usize), + Output(usize), +} + +/// Object, which builds an initial transaction in a fuzzing step to make it valid after +/// generating of script hashes after program compiling. +pub struct FinalTransactionBuilder { + initial_tx: FinalTransaction, + targets: Vec, + post_hook: Option>, +} + +impl FinalTransactionBuilder { + /// Creates a reusable fuzz transaction blueprint. + /// + /// # Errors + /// Returns an error when there are no targets, a target is duplicated, or a target index is out of bounds. + pub fn new( + initial_tx: FinalTransaction, + targets: impl IntoIterator, + ) -> Result { + let targets = targets.into_iter().collect::>(); + + Self::validate_targets(&initial_tx, &targets)?; + + Ok(Self { + initial_tx, + targets, + post_hook: None, + }) + } + + #[must_use] + pub fn with_post_hook( + mut self, + hook: impl Fn(&mut FinalTransaction, &Script, &Arguments, &WitnessValues) -> Result<(), FuzzError> + 'static, + ) -> Self { + self.post_hook = Some(Box::new(hook)); + self + } + + #[must_use] + pub fn targets(&self) -> &[ProgramTarget] { + &self.targets + } + + /// Replaces the scriptPubKey at a selected input or output. + /// + /// # Errors + /// Returns an error when the selected target index is out of bounds. + pub fn set_program_script( + tx: &mut FinalTransaction, + target: ProgramTarget, + script: &Script, + ) -> Result<(), FuzzError> { + match target { + ProgramTarget::Input(index) => { + let input_count = tx.n_inputs(); + let input = tx + .inputs_mut() + .get_mut(index) + .ok_or(FuzzError::InputTargetOutOfBounds { index, input_count })?; + input.partial_input.witness_utxo.script_pubkey = script.clone(); + } + ProgramTarget::Output(index) => { + let output_count = tx.n_outputs(); + let output = tx + .outputs_mut() + .get_mut(index) + .ok_or(FuzzError::OutputTargetOutOfBounds { index, output_count })?; + output.script_pubkey = script.clone(); + } + } + + Ok(()) + } + + pub(crate) fn prepare_transaction( + &self, + program: &Program, + script: &Script, + arguments: &Arguments, + witness: &WitnessValues, + ) -> Result { + let mut tx = self.initial_tx.clone(); + + for target in self.targets.iter().copied() { + Self::set_program_script(&mut tx, target, script)?; + + if let ProgramTarget::Input(index) = target { + let input_count = tx.n_inputs(); + let input = tx + .inputs_mut() + .get_mut(index) + .ok_or(FuzzError::InputTargetOutOfBounds { index, input_count })?; + input.program_input = Some(ProgramInput::new(Box::new(program.clone()), witness.clone())); + } + } + + if let Some(post_hook) = &self.post_hook { + post_hook(&mut tx, script, arguments, witness)?; + } + + Ok(tx) + } + + fn validate_targets(initial_tx: &FinalTransaction, targets: &[ProgramTarget]) -> Result<(), FuzzError> { + if targets.is_empty() { + return Err(FuzzError::NoProgramTargets); + } + + let mut unique_targets = HashSet::with_capacity(targets.len()); + + for target in targets.iter().copied() { + if !unique_targets.insert(target) { + return Err(FuzzError::DuplicateProgramTarget(target)); + } + + match target { + ProgramTarget::Input(index) if index >= initial_tx.n_inputs() => { + return Err(FuzzError::InputTargetOutOfBounds { + index, + input_count: initial_tx.n_inputs(), + }); + } + ProgramTarget::Output(index) if index >= initial_tx.n_outputs() => { + return Err(FuzzError::OutputTargetOutOfBounds { + index, + output_count: initial_tx.n_outputs(), + }); + } + ProgramTarget::Input(_) | ProgramTarget::Output(_) => {} + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::cell::Cell; + use std::rc::Rc; + + use simplicityhl::elements::hashes::Hash; + use simplicityhl::elements::{OutPoint, Script, TxOut, Txid}; + + use smplx_sdk::provider::SimplicityNetwork; + use smplx_sdk::transaction::{PartialInput, PartialOutput, RequiredSignature, UTXO}; + + use super::*; + + const DUMMY_PROGRAM: &str = r" + fn main() { + assert!(true); + } + "; + + fn initial_transaction() -> FinalTransaction { + let network = SimplicityNetwork::default_regtest(); + let mut tx = FinalTransaction::new(); + + for vout in 0..2 { + let utxo = UTXO { + outpoint: OutPoint::new(Txid::all_zeros(), vout), + txout: TxOut::new_fee(1_000, network.policy_asset()), + secrets: None, + }; + tx.add_input(PartialInput::new(utxo), RequiredSignature::None); + tx.add_output(PartialOutput::new(Script::new(), 500, network.policy_asset())); + } + + tx + } + + fn prepared_program() -> (Program, Script) { + let program = Program::new(DUMMY_PROGRAM, Arguments::default()); + let script = program.get_script_pubkey(&SimplicityNetwork::default_regtest()); + (program, script) + } + + #[test] + fn rejects_empty_duplicate_and_out_of_bounds_targets() { + assert_eq!( + FinalTransactionBuilder::new(initial_transaction(), []).err(), + Some(FuzzError::NoProgramTargets) + ); + assert_eq!( + FinalTransactionBuilder::new( + initial_transaction(), + [ProgramTarget::Input(0), ProgramTarget::Input(0)], + ) + .err(), + Some(FuzzError::DuplicateProgramTarget(ProgramTarget::Input(0))) + ); + assert_eq!( + FinalTransactionBuilder::new(initial_transaction(), [ProgramTarget::Output(2)]).err(), + Some(FuzzError::OutputTargetOutOfBounds { + index: 2, + output_count: 2, + }) + ); + } + + #[test] + fn injects_program_into_all_targets_without_consuming_them() { + let builder = FinalTransactionBuilder::new( + initial_transaction(), + [ProgramTarget::Input(0), ProgramTarget::Output(1)], + ) + .unwrap(); + let (program, script) = prepared_program(); + let arguments = Arguments::default(); + let witness = WitnessValues::default(); + + let first = builder + .prepare_transaction(&program, &script, &arguments, &witness) + .unwrap(); + let second = builder + .prepare_transaction(&program, &script, &arguments, &witness) + .unwrap(); + + assert_eq!(builder.targets().len(), 2); + assert!(first.inputs()[0].program_input.is_some()); + assert_eq!(first.inputs()[0].partial_input.witness_utxo.script_pubkey, script); + assert_eq!(first.outputs()[1].script_pubkey, script); + assert!(second.inputs()[0].program_input.is_some()); + assert_eq!(second.outputs()[1].script_pubkey, script); + } + + #[test] + fn runs_post_hook_after_program_injection_and_propagates_errors() { + let hook_ran = Rc::new(Cell::new(false)); + let hook_ran_in_callback = Rc::clone(&hook_ran); + let builder = FinalTransactionBuilder::new(initial_transaction(), [ProgramTarget::Input(0)]) + .unwrap() + .with_post_hook(move |tx, script, _, _| { + assert_eq!(tx.inputs()[0].partial_input.witness_utxo.script_pubkey, *script); + hook_ran_in_callback.set(true); + FinalTransactionBuilder::set_program_script(tx, ProgramTarget::Input(0), &Script::new()) + }); + let (program, script) = prepared_program(); + + let prepared = builder + .prepare_transaction(&program, &script, &Arguments::default(), &WitnessValues::default()) + .unwrap(); + + assert!(hook_ran.get()); + assert!(prepared.inputs()[0].partial_input.witness_utxo.script_pubkey.is_empty()); + + let failing_builder = FinalTransactionBuilder::new(initial_transaction(), [ProgramTarget::Input(0)]) + .unwrap() + .with_post_hook(|_, _, _, _| Err(FuzzError::PostHook("hook failed".to_string()))); + let error = failing_builder + .prepare_transaction(&program, &script, &Arguments::default(), &WitnessValues::default()) + .unwrap_err(); + + assert_eq!(error, FuzzError::PostHook("hook failed".to_string())); + } +} diff --git a/crates/test/src/fuzz/builders/mod.rs b/crates/test/src/fuzz/builders/mod.rs new file mode 100644 index 00000000..43f873f4 --- /dev/null +++ b/crates/test/src/fuzz/builders/mod.rs @@ -0,0 +1,3 @@ +mod final_tx_builder; + +pub use final_tx_builder::*; diff --git a/crates/test/src/fuzz/core.rs b/crates/test/src/fuzz/core.rs new file mode 100644 index 00000000..22af9c5b --- /dev/null +++ b/crates/test/src/fuzz/core.rs @@ -0,0 +1,82 @@ +use std::sync::Arc; + +use simplicityhl::elements::Script; +use simplicityhl::elements::pset::PartiallySignedTransaction; +use simplicityhl::simplicity::{RedeemNode, Value}; +use simplicityhl::{Arguments, WitnessValues}; + +use smplx_sdk::program::{Program, ProgramError, ProgramFactory}; +use smplx_sdk::provider::{ProviderTrait, SimplicityNetwork}; +use smplx_sdk::signer::{Signer, SignerError}; +use smplx_sdk::transaction::FinalTransaction; + +use crate::context::TestContext; +#[derive(Clone, Debug)] +pub(crate) enum SignerOption { + DefaultTestConfigSigner, + CustomSigner, + NoSigning, +} + +#[derive(Clone)] +pub struct FuzzContext { + pub(crate) signer: Arc>, + pub mock_provider: Arc, + /// Used for inserting signer + pub(crate) test_context: Arc>, + pub(crate) signer_option: SignerOption, + pub network: SimplicityNetwork, +} + +pub type ProgramExecResult = Result<(Arc, Value), ProgramError>; + +pub trait FuzzableProgram>: AsRef { + fn build_program(args: impl Into, network: &SimplicityNetwork) -> (Box

, Script); +} + +impl + ProgramFactory

> FuzzableProgram

for P { + fn build_program(args: impl Into, network: &SimplicityNetwork) -> (Box

, Script) { + let prog = P::instantiate_program(args); + let script = prog.as_ref().as_ref().get_script_pubkey(network); + (prog, script) + } +} + +impl FuzzContext { + pub fn get_signer(&self) -> Option<&Signer> { + match self.signer_option { + SignerOption::DefaultTestConfigSigner => Some( + self.test_context + .as_ref() + .as_ref() + .expect("TestContext has to be unempty in order to get a default signer") + .get_default_signer(), + ), + SignerOption::CustomSigner => self.signer.as_ref().as_ref(), + SignerOption::NoSigning => None, + } + } + + #[inline] + pub fn sign_or_extract(&self, ft: &FinalTransaction) -> Result { + match &self.signer_option { + SignerOption::DefaultTestConfigSigner | SignerOption::CustomSigner => { + let signer = self.get_signer(); + Ok(signer.unwrap().sign_tx(ft)?) + } + SignerOption::NoSigning => Ok(ft.extract_pst().0), + } + } +} + +pub trait ProgramCheck { + fn call( + &self, + ctx: &FuzzContext, + tx: &PartiallySignedTransaction, + arguments: &Arguments, + witness: &WitnessValues, + input_index: usize, + program_exec_result: ProgramExecResult, + ) -> Result<(), String>; +} diff --git a/crates/test/src/fuzz/engine.rs b/crates/test/src/fuzz/engine.rs new file mode 100644 index 00000000..e1d2118f --- /dev/null +++ b/crates/test/src/fuzz/engine.rs @@ -0,0 +1,314 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::sync::Arc; + +use simplicityhl::{Arguments, WitnessValues}; + +use proptest::prelude::{BoxedStrategy, TestCaseError}; +use proptest::strategy::{Strategy, ValueTree}; +use proptest::test_runner::TestRunner; + +use smplx_sdk::program::{ArgumentsTrait, ProgramFactory, ProgramTrait, RandomArguments, RandomWitness, WitnessTrait}; +use smplx_sdk::provider::{EsploraProvider, ProviderTrait, SimplicityNetwork}; +use smplx_sdk::signer::Signer; + +use crate::context::TestContext; +use crate::fuzz::args_strategy::{InterestingRandom, Random, RandomValuePool}; +use crate::fuzz::builders::{FinalTransactionBuilder, ProgramTarget}; +use crate::fuzz::core::{FuzzContext, FuzzableProgram, SignerOption}; +use crate::fuzz::{ProgramCheck, ProgramExecResult}; + +pub struct SimplexFuzzEngine { + runner: TestRunner, + fuzz_context: FuzzContext, + local_fuzz_config: LocalFuzzConfig, + strategy_storage: BoxedStrategy<(Arguments, WitnessValues)>, + blueprint: FinalTransactionBuilder, + _phantom: PhantomData<(Program, Args, Wit)>, +} + +pub struct LocalFuzzConfig { + runs: usize, +} + +pub struct FuzzEngineBuilder { + proptest_config: proptest::test_runner::Config, + pub(crate) signer_option: SignerOption, + pub(crate) signer: Option, + test_context: Option, + mock_provider: Arc, + network: SimplicityNetwork, + _phantom: PhantomData<(Program, Args, Wit)>, +} + +impl FuzzEngineBuilder +where + Program: FuzzableProgram + ProgramFactory + Clone + 'static, + Args: ArgumentsTrait + RandomArguments + std::fmt::Debug + Clone + 'static, + Wit: WitnessTrait + RandomWitness + std::fmt::Debug + Clone + 'static, +{ + pub fn new(config: proptest::test_runner::Config) -> Self { + let default_network = SimplicityNetwork::default_regtest(); + + Self { + proptest_config: config, + test_context: None, + signer_option: SignerOption::NoSigning, + signer: None, + mock_provider: Arc::new(Self::get_default_provider(default_network)), + network: default_network, + _phantom: PhantomData, + } + } + + pub fn from_context(mut config: proptest::test_runner::Config, test_context: TestContext) -> Self { + let default_network = SimplicityNetwork::default_regtest(); + let smplx_test_context = test_context.get_config(); + if let Some(proptest_conf) = smplx_test_context.proptest.as_ref() { + if let Some(cases) = proptest_conf.cases { + config.cases = cases; + } + + if let Some(max_global_rejects) = proptest_conf.max_global_rejects { + config.max_global_rejects = max_global_rejects; + } + + if let Some(max_shrink_iters) = proptest_conf.max_shrink_iters { + config.max_shrink_iters = max_shrink_iters; + } + + if let Some(max_local_rejects) = proptest_conf.max_local_rejects { + config.max_local_rejects = max_local_rejects; + } + + config.verbose = smplx_test_context.verbosity as u32; + } + + Self { + proptest_config: config, + test_context: Some(test_context), + signer_option: SignerOption::NoSigning, + signer: None, + mock_provider: Arc::new(Self::get_default_provider(default_network)), + network: default_network, + _phantom: PhantomData, + } + } + + fn get_default_provider(default_network: SimplicityNetwork) -> EsploraProvider { + EsploraProvider::new("default_web_page.com".into(), default_network) + } +} + +impl FuzzEngineBuilder +where + Program: FuzzableProgram + ProgramFactory + Clone + 'static, + Args: ArgumentsTrait + RandomArguments + std::fmt::Debug + Clone + 'static, + Wit: WitnessTrait + RandomWitness + std::fmt::Debug + Clone + 'static, +{ + pub fn with_signer(mut self, signer: Signer) -> Self { + self.signer_option = SignerOption::CustomSigner; + self.signer = Some(signer); + self + } + + pub fn with_default_signer(mut self) -> Self { + self.signer_option = SignerOption::DefaultTestConfigSigner; + self + } + + pub fn build( + self, + strategy_storage: impl Strategy + 'static, + blueprint: FinalTransactionBuilder, + ) -> SimplexFuzzEngine { + // TODO: add fetching of failure values to feed correct seed into TestRunner on creation + + SimplexFuzzEngine { + runner: proptest::test_runner::TestRunner::new(self.proptest_config), + fuzz_context: FuzzContext { + #[allow(clippy::arc_with_non_send_sync)] + signer: Arc::new(self.signer), + mock_provider: self.mock_provider, + #[allow(clippy::arc_with_non_send_sync)] + test_context: Arc::new(self.test_context), + signer_option: self.signer_option, + network: self.network, + }, + local_fuzz_config: LocalFuzzConfig { runs: 500 }, + strategy_storage: strategy_storage.boxed(), + blueprint, + _phantom: PhantomData, + } + } +} + +pub struct FuzzStrategyBuilder> { + base_strat: Option, + _placeholder: PhantomData<(Args, Wit)>, +} + +impl FuzzStrategyBuilder { + pub fn new() -> Self { + Self::default() + } +} + +impl Default for FuzzStrategyBuilder { + fn default() -> Self { + Self { + base_strat: Some(InterestingRandom::default()), + _placeholder: Default::default(), + } + } +} + +impl FuzzStrategyBuilder { + pub fn with_random(self) -> FuzzStrategyBuilder> { + FuzzStrategyBuilder { + base_strat: Some(Random::::default()), + _placeholder: Default::default(), + } + } + + pub fn with_random_pool(self) -> FuzzStrategyBuilder> { + FuzzStrategyBuilder { + base_strat: Some(RandomValuePool::::default()), + _placeholder: Default::default(), + } + } + + pub fn with_custom_strategy(self, custom_strat: NewStrat) -> FuzzStrategyBuilder + where + NewStrat: Strategy + 'static, + { + FuzzStrategyBuilder { + base_strat: Some(custom_strat), + _placeholder: Default::default(), + } + } + + pub fn with_random_interesting_values(self) -> FuzzStrategyBuilder> { + FuzzStrategyBuilder { + base_strat: Some(InterestingRandom::::default()), + _placeholder: Default::default(), + } + } +} + +impl FuzzStrategyBuilder +where + BaseStrat: Strategy + 'static, +{ + pub fn build(self) -> BoxedStrategy<(Arguments, WitnessValues)> { + let base = self + .base_strat + .expect("Base strategy is mandatory. Call with_random() or similar."); + base.boxed() + } +} + +// TODO: create a shared state with Atomic counter (for multiple runners) + +#[derive(Debug)] +pub struct CaseOutcome {} + +/// Returned by a single fuzz when a counterexample has been discovered +#[derive(Debug)] +pub struct CounterExampleOutcome { + pub args: Arguments, + pub wit: WitnessValues, +} + +/// Outcome of a single fuzz +#[derive(Debug)] +pub enum FuzzOutcome { + Case(CaseOutcome), + CounterExample(CounterExampleOutcome), +} + +impl SimplexFuzzEngine +where + Program: FuzzableProgram + ProgramFactory + Clone + 'static, +{ + pub fn run_with_check(self, program_post_hook: impl ProgramCheck) { + let mut runner = self.runner; + let context = self.fuzz_context; + let blueprint = self.blueprint; + let strategy_storage = self.strategy_storage; + + // TODO: add collected strategies usage to modify FinalTransaction or inputs into `proptest::prop_oneof[ strategy list ... ]` + + let mut runner_cnt = 0; + while runner_cnt < self.local_fuzz_config.runs { + // TODO: If counterexample recorded, replay it first, without incrementing runs. + // TODO: evaluate incremental runs + let mut inc_runs = || { + debug_assert!( + runner_cnt <= self.local_fuzz_config.runs, + "worker runs were not distributed correctly" + ); + runner_cnt += 1; + }; + + match Self::single_fuzz(&mut runner, &context, &blueprint, &program_post_hook, &strategy_storage) { + Ok(fuzz_outcome) => match fuzz_outcome { + FuzzOutcome::Case(_case) => { + inc_runs(); + } + FuzzOutcome::CounterExample(CounterExampleOutcome { args, wit }) => { + panic!("program failed tith these arguments: {args} and witness: {wit}"); + } + }, + Err(err) => match err { + TestCaseError::Fail(e) => { + panic!("{e}"); + } + TestCaseError::Reject(e) => { + panic!("{e}"); + } + }, + } + } + } + + fn single_fuzz( + test_runner: &mut TestRunner, + fuzz_context: &FuzzContext, + initial_tx: &FinalTransactionBuilder, + program_post_hook: &impl ProgramCheck, + strategy: &BoxedStrategy<(Arguments, WitnessValues)>, + ) -> Result { + // TODO: extract seed in order to save it in failure case + let (arguments, witness) = strategy + .new_tree(test_runner) + .map_err(|error| TestCaseError::fail(format!("failed to generate fuzz inputs: {error}")))? + .current(); + let (program, script) = Program::build_program(arguments.clone(), &fuzz_context.network); + let final_transaction = initial_tx + .prepare_transaction(program.as_ref().as_ref(), &script, &arguments, &witness) + .map_err(|error| TestCaseError::fail(format!("failed to prepare fuzz transaction: {error}")))?; + let pst = fuzz_context.sign_or_extract(&final_transaction)?; + + // Iterate over program inputs to check contract execution + for target in initial_tx.targets().iter().copied() { + let ProgramTarget::Input(input_index) = target else { + continue; + }; + + let exec_result: ProgramExecResult = + program + .as_ref() + .as_ref() + .execute(&pst, &witness, input_index, &fuzz_context.network); + if let Err(error) = + program_post_hook.call(fuzz_context, &pst, &arguments, &witness, input_index, exec_result) + { + return Err(TestCaseError::fail(format!( + "{error}, args: {arguments}, wit: {witness}" + ))); + } + } + Ok(FuzzOutcome::Case(CaseOutcome {})) + } +} diff --git a/crates/test/src/fuzz/mod.rs b/crates/test/src/fuzz/mod.rs new file mode 100644 index 00000000..c9a01d32 --- /dev/null +++ b/crates/test/src/fuzz/mod.rs @@ -0,0 +1,12 @@ +pub mod args_strategy; +pub mod builders; +pub mod core; +pub mod engine; +pub mod utils; + +pub use proptest; + +pub use crate::error::FuzzError; +pub use core::{FuzzContext, FuzzableProgram, ProgramCheck, ProgramExecResult}; +pub use engine::{FuzzEngineBuilder, SimplexFuzzEngine}; +pub use utils::generate_value_by_ty; diff --git a/crates/test/src/fuzz/utils.rs b/crates/test/src/fuzz/utils.rs new file mode 100644 index 00000000..7214e37a --- /dev/null +++ b/crates/test/src/fuzz/utils.rs @@ -0,0 +1,45 @@ +use simplicityhl::num::U256; +use simplicityhl::types::{TypeInner, UIntType}; +use simplicityhl::value::ValueConstructible; +use simplicityhl::{ResolvedType, Value}; + +use rand::Rng; + +pub fn generate_value_by_ty(ty: &ResolvedType, rng: &mut R) -> Value { + match ty.as_inner() { + TypeInner::Either(left_ty, right_ty) => match rng.random::() { + true => Value::left(generate_value_by_ty(left_ty, rng), (**right_ty).clone()), + false => Value::right((**left_ty).clone(), generate_value_by_ty(right_ty, rng)), + }, + TypeInner::Option(option_ty) => match rng.random::() { + true => Value::some(generate_value_by_ty(option_ty, rng)), + false => Value::none((**option_ty).clone()), + }, + TypeInner::Boolean => Value::from(rng.random::()), + TypeInner::UInt(x) => match x { + UIntType::U1 => Value::u1(rng.random::() & 0x0001), + UIntType::U2 => Value::u2(rng.random::() & 0x0003), + UIntType::U4 => Value::u4(rng.random::() & 0x000F), + UIntType::U8 => Value::u8(rng.random::()), + UIntType::U16 => Value::u16(rng.random::()), + UIntType::U32 => Value::u32(rng.random::()), + UIntType::U64 => Value::u64(rng.random::()), + UIntType::U128 => Value::u128(rng.random::()), + UIntType::U256 => Value::u256(U256::from_byte_array(rng.random())), + }, + TypeInner::Tuple(tuple_ty) => Value::tuple(tuple_ty.iter().map(|x| generate_value_by_ty(x, rng))), + TypeInner::Array(array_ty, size) => Value::array( + (0..*size).map(|_| generate_value_by_ty(array_ty, rng)), + (**array_ty).clone(), + ), + TypeInner::List(list_ty, size_pow_2) => { + let size = rng.random_range(0..size_pow_2.get()); + Value::list( + (0..size).map(|_| generate_value_by_ty(list_ty, rng)), + (**list_ty).clone(), + *size_pow_2, + ) + } + _ => Value::unit(), + } +} diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 2725aa8a..f3a2ab01 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -1,6 +1,7 @@ pub mod config; pub mod context; pub mod error; +pub mod fuzz; pub mod macros; pub mod network_utils; diff --git a/crates/test/src/macros/core.rs b/crates/test/src/macros/core.rs index c6ecbbaf..949de3eb 100644 --- a/crates/test/src/macros/core.rs +++ b/crates/test/src/macros/core.rs @@ -8,19 +8,30 @@ macro_rules! smplx_test_marker { () => { "_smplx_test" }; + (fuzz) => { + "_smplx_fuzz" + }; } pub const SMPLX_TEST_MARKER: &str = smplx_test_marker!(); +pub const SMPLX_FUZZ_MARKER: &str = smplx_test_marker!(fuzz); type AttributeArgs = syn::punctuated::Punctuated; -pub fn expand(args: TokenStream, input: syn::ItemFn) -> syn::Result { +pub fn expand_test(args: TokenStream, input: syn::ItemFn) -> syn::Result { let parser = AttributeArgs::parse_terminated; let args = parser.parse2(args)?; expand_inner(&input, args) } +pub fn expand_fuzz(args: TokenStream, input: syn::ItemFn) -> syn::Result { + let parser = AttributeArgs::parse_terminated; + let args = parser.parse2(args)?; + + expand_fuzz_inner(&input, args) +} + // TODO: args? fn expand_inner(input: &syn::ItemFn, _args: AttributeArgs) -> syn::Result { let ret = &input.sig.output; @@ -57,3 +68,54 @@ fn expand_inner(input: &syn::ItemFn, _args: AttributeArgs) -> syn::Result syn::Result { + let ret = &input.sig.output; + let name = quote::format_ident!("{}_{}", &input.sig.ident.to_string(), SMPLX_FUZZ_MARKER); + let inputs = &input.sig.inputs; + let body = &input.block; + let attrs = &input.attrs; + + let simplex_test_env = TEST_ENV_NAME; + + let expansion = quote::quote! { + #[::core::prelude::v1::test] + #(#attrs)* + fn #name() #ret { + use std::path::PathBuf; + use simplex::TestContext; + + fn #name(#inputs) #ret { + #body + } + + let config = fuzz::proptest::test_runner::Config { + test_name: ::core::option::Option::Some(::core::concat!( + ::core::module_path!(), + "::", + ::core::stringify!(#name) + )), + source_file: Some(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/", + stringify!(#name), + ".txt" + )), + ..Default::default() + }; + let test_context = match std::env::var(#simplex_test_env) { + Err(_) => { + panic!("Failed to run this test, required to use `simplex test`"); + }, + Ok(path) => { + TestContext::new(PathBuf::from(path)).unwrap() + } + }; + let fuzz_context_builder = FuzzEngineBuilder::from_context(config, test_context); + #name(fuzz_context_builder) + } + }; + + Ok(expansion) +} diff --git a/crates/test/src/macros/mod.rs b/crates/test/src/macros/mod.rs index 9e9d50f0..f266cc16 100644 --- a/crates/test/src/macros/mod.rs +++ b/crates/test/src/macros/mod.rs @@ -1,3 +1,3 @@ pub mod core; -pub use core::expand; +pub use core::{expand_fuzz, expand_test}; diff --git a/crates/wasm/src/lib.rs b/crates/wasm/src/lib.rs index 275c3b1e..0198788d 100644 --- a/crates/wasm/src/lib.rs +++ b/crates/wasm/src/lib.rs @@ -86,6 +86,12 @@ impl ArgumentsTrait for FixedArguments { } } +impl From for Arguments { + fn from(val: FixedArguments) -> Self { + val.build_arguments() + } +} + /// Witness values for a covenant input, resolved before the transaction is assembled. #[derive(Clone)] struct FixedWitness(WitnessValues); @@ -96,6 +102,12 @@ impl WitnessTrait for FixedWitness { } } +impl From for WitnessValues { + fn from(val: FixedWitness) -> Self { + val.build_witness() + } +} + /// A compiled `SimplicityHL` covenant. #[wasm_bindgen] pub struct Covenant { @@ -189,7 +201,7 @@ impl Covenant { _ => Arguments::default(), }; - let mut program = Program::new(Arc::::from(source), &FixedArguments(arguments)); + let mut program = Program::new(Arc::::from(source), FixedArguments(arguments)); if let Some(include) = include_debug_symbols { program = program.with_debug_symbols(include); @@ -587,7 +599,7 @@ impl TransactionBuilder { program_input .program - .execute(&pst, &program_input.witness.build_witness(), input_index, &network) + .execute(&pst, &program_input.witness, input_index, &network) .map_err(|e| JsError::new(&format!("Input {input_index} did not execute: {e}")))?; Ok(()) @@ -674,7 +686,7 @@ impl TransactionBuilder { Ok(ProgramInput { program: Box::new(Covenant::new(source, arguments_json, extra_leaves_json, include_debug_symbols)?.program), - witness: Box::new(FixedWitness(witness)), + witness: FixedWitness(witness).into(), }) } } diff --git a/examples/basic/Cargo.lock b/examples/basic/Cargo.lock index 6d165853..e2440c05 100644 --- a/examples/basic/Cargo.lock +++ b/examples/basic/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -69,24 +69,34 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ "object", ] +[[package]] +name = "ariadne" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "autocfg" @@ -96,9 +106,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,21 +116,22 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] name = "base58ck" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec5dc7e09f7bb15f0062da7c03086d6b71a2c84e0af4fccbbc7d8c6559847816" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" dependencies = [ "bitcoin_hashes", ] @@ -150,17 +161,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ "bitcoin_hashes", - "rand", - "rand_core", + "rand 0.8.8", + "rand_core 0.6.4", "serde", "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcoin" -version = "0.32.100" +version = "0.32.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39581299241111285f3268ba75ddf372746fd041620918b145c1af9d75e91b6c" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" dependencies = [ "base58ck", "base64 0.21.7", @@ -168,17 +194,37 @@ dependencies = [ "bitcoin-io", "bitcoin-units", "bitcoin_hashes", - "hex-conservative", + "hex-conservative 0.2.2", "hex_lit", "secp256k1", "serde", ] +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] [[package]] name = "bitcoin-private" @@ -188,21 +234,22 @@ checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" [[package]] name = "bitcoin-units" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bad157b78d0d1b22c4cbb6a35a566211fc4d14866a37f2c780652b50f3b845" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" dependencies = [ + "bitcoin-consensus-encoding", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.100" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", "serde", ] @@ -251,9 +298,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -266,12 +313,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", - "serde", + "serde_core", ] [[package]] @@ -288,9 +335,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.63" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -320,18 +367,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -371,9 +418,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -381,18 +428,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crypto-common" @@ -429,9 +476,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" dependencies = [ "serde", ] @@ -463,9 +510,9 @@ dependencies = [ [[package]] name = "elements" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b2569d3495bfdfce36c504fd4d78752ff4a7699f8a33e6f3ee523bddf9f6ad" +checksum = "83c63a04238f4b7f3564fe9705921452c2900efd00982545e5c634fac9024fe2" dependencies = [ "bech32", "bitcoin", @@ -504,15 +551,21 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" @@ -528,21 +581,21 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", @@ -587,15 +640,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] @@ -606,21 +657,21 @@ checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.14", - "regex-syntax 0.8.10", + "regex-automata 0.4.18", + "regex-syntax 0.8.11", ] [[package]] @@ -629,7 +680,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "ignore", "walkdir", ] @@ -651,12 +702,6 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hex" version = "0.4.3" @@ -672,6 +717,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -696,23 +750,17 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ignore" -version = "0.4.26" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.14", + "regex-automata 0.4.18", "same-file", "walkdir", "winapi-util", @@ -726,8 +774,6 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", - "serde", - "serde_core", ] [[package]] @@ -753,19 +799,19 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.100" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -784,17 +830,11 @@ dependencies = [ "serde_json", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -810,15 +850,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memoffset" @@ -876,11 +916,20 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" -version = "0.37.3" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "memchr", ] @@ -915,6 +964,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -931,33 +986,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.13.1", + "num-traits", + "rand 0.9.4", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax 0.8.11", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -976,13 +1056,23 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -992,7 +1082,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[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.5", ] [[package]] @@ -1004,6 +1104,24 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "regex-automata" version = "0.3.9" @@ -1017,13 +1135,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.10", + "regex-syntax 0.8.11", ] [[package]] @@ -1034,9 +1152,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "ring" @@ -1058,7 +1176,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -1071,7 +1189,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -1080,9 +1198,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -1095,18 +1213,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -1116,9 +1234,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] [[package]] name = "same-file" @@ -1136,7 +1266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand", + "rand 0.8.8", "secp256k1-sys", "serde", ] @@ -1157,7 +1287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" dependencies = [ "bitcoin-private", - "rand", + "rand 0.8.8", "secp256k1", "secp256k1-zkp-sys", "serde", @@ -1181,9 +1311,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1191,22 +1321,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] @@ -1268,7 +1398,7 @@ dependencies = [ "elements", "getrandom 0.2.17", "ghost-cell", - "hex-conservative", + "hex-conservative 0.2.2", "miniscript", "simplicity-sys", ] @@ -1285,10 +1415,11 @@ dependencies = [ [[package]] name = "simplicityhl" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361316795ec753230c421d964ab60940d20d5252c2d39272452528832c9e0ec5" +checksum = "ae77168fa3367d234b11bb3999cbd6e04d7180db0b9e11f83de259528e3b23d1" dependencies = [ + "ariadne", "base64 0.21.7", "chumsky", "clap", @@ -1296,6 +1427,7 @@ dependencies = [ "getrandom 0.2.17", "itertools", "miniscript", + "semver", "serde", "serde_json", "simplicity-lang", @@ -1320,7 +1452,7 @@ dependencies = [ "serde", "serde_json", "simplicityhl", - "syn", + "syn 2.0.119", "thiserror", "toml", "toml_edit", @@ -1332,7 +1464,7 @@ version = "0.0.10" dependencies = [ "smplx-build", "smplx-test", - "syn", + "syn 2.0.119", ] [[package]] @@ -1360,6 +1492,7 @@ dependencies = [ "elements-miniscript", "hex", "minreq 3.0.0", + "rand_core 0.9.5", "serde", "serde_json", "sha2", @@ -1372,6 +1505,8 @@ name = "smplx-std" version = "0.0.10" dependencies = [ "either", + "rand 0.9.4", + "rand_core 0.9.5", "serde", "simplicityhl", "smplx-macros", @@ -1385,21 +1520,23 @@ version = "0.0.10" dependencies = [ "electrsd", "proc-macro2", + "proptest", "quote", + "rand 0.9.4", "serde", "simplicityhl", "smplx-regtest", "smplx-sdk", - "syn", + "syn 2.0.119", "thiserror", "toml", ] [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -1422,9 +1559,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -1438,7 +1586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -1446,29 +1594,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -1522,16 +1670,16 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -1546,6 +1694,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1568,10 +1722,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] -name = "unicode-xid" -version = "0.2.6" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "untrusted" @@ -1591,6 +1745,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -1609,27 +1772,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1640,9 +1794,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1650,65 +1804,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.0", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -1839,22 +1959,13 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" @@ -1862,102 +1973,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" +name = "yansi" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.0", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1968,6 +2006,6 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/examples/basic/Simplex.toml b/examples/basic/Simplex.toml index c522f64c..f3ae2c0b 100644 --- a/examples/basic/Simplex.toml +++ b/examples/basic/Simplex.toml @@ -21,6 +21,12 @@ # bitcoins = 10_000_000 # verbosity = 0 +# [test.fuzz] +# cases = 10000 +# max_shrink_iters = 100 +# max_global_rejects = 100 +# max_local_rejects = 100 + # [test.esplora] # url = "" # network = "" diff --git a/examples/basic/tests/basic_test.rs b/examples/basic/tests/basic_test.rs index 9253626a..ed219376 100644 --- a/examples/basic/tests/basic_test.rs +++ b/examples/basic/tests/basic_test.rs @@ -12,7 +12,7 @@ fn get_p2pk(context: &simplex::TestContext) -> (P2pkProgram, Script) { public_key: signer.get_schnorr_public_key().serialize(), }; - let p2pk = P2pkProgram::new(&arguments); + let p2pk = P2pkProgram::new(arguments); let p2pk_script = p2pk.get_script_pubkey(context.get_network()); (p2pk, p2pk_script) @@ -43,7 +43,7 @@ fn spend_p2pk(context: &simplex::TestContext) -> anyhow::Result> { ft.add_program_input( PartialInput::new(p2pk_utxos[0].clone()), - ProgramInput::new(Box::new(p2pk.as_ref().clone()), Box::new(witness.clone())), + ProgramInput::new(Box::new(p2pk.as_ref().clone()), witness.clone()), RequiredSignature::Witness("SIGNATURE".to_string()), ); diff --git a/fixtures/.gitignore b/fixtures/.gitignore index 327555cd..c923eff2 100644 --- a/fixtures/.gitignore +++ b/fixtures/.gitignore @@ -1 +1,5 @@ src/artifacts + +deps/* +!deps/math +!deps/merkle diff --git a/fixtures/Cargo.lock b/fixtures/Cargo.lock index 56f00393..5ed8bbfa 100644 --- a/fixtures/Cargo.lock +++ b/fixtures/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -69,24 +69,34 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ "object", ] +[[package]] +name = "ariadne" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "autocfg" @@ -96,9 +106,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,21 +116,22 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] name = "base58ck" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec5dc7e09f7bb15f0062da7c03086d6b71a2c84e0af4fccbbc7d8c6559847816" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" dependencies = [ "bitcoin_hashes", ] @@ -150,17 +161,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ "bitcoin_hashes", - "rand", - "rand_core", + "rand 0.8.8", + "rand_core 0.6.4", "serde", "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcoin" -version = "0.32.100" +version = "0.32.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39581299241111285f3268ba75ddf372746fd041620918b145c1af9d75e91b6c" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" dependencies = [ "base58ck", "base64 0.21.7", @@ -168,17 +194,37 @@ dependencies = [ "bitcoin-io", "bitcoin-units", "bitcoin_hashes", - "hex-conservative", + "hex-conservative 0.2.2", "hex_lit", "secp256k1", "serde", ] +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] [[package]] name = "bitcoin-private" @@ -188,21 +234,22 @@ checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" [[package]] name = "bitcoin-units" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bad157b78d0d1b22c4cbb6a35a566211fc4d14866a37f2c780652b50f3b845" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" dependencies = [ + "bitcoin-consensus-encoding", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.100" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", "serde", ] @@ -251,9 +298,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -266,12 +313,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", - "serde", + "serde_core", ] [[package]] @@ -288,9 +335,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.63" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -320,18 +367,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -371,9 +418,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -381,18 +428,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crypto-common" @@ -429,9 +476,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" dependencies = [ "serde", ] @@ -463,9 +510,9 @@ dependencies = [ [[package]] name = "elements" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b2569d3495bfdfce36c504fd4d78752ff4a7699f8a33e6f3ee523bddf9f6ad" +checksum = "83c63a04238f4b7f3564fe9705921452c2900efd00982545e5c634fac9024fe2" dependencies = [ "bech32", "bitcoin", @@ -504,15 +551,21 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" @@ -528,21 +581,21 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", @@ -587,15 +640,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] @@ -606,21 +657,21 @@ checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.14", - "regex-syntax 0.8.10", + "regex-automata 0.4.18", + "regex-syntax 0.8.11", ] [[package]] @@ -629,7 +680,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "ignore", "walkdir", ] @@ -651,12 +702,6 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hex" version = "0.4.3" @@ -672,6 +717,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -696,23 +750,17 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ignore" -version = "0.4.26" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.14", + "regex-automata 0.4.18", "same-file", "walkdir", "winapi-util", @@ -726,8 +774,6 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", - "serde", - "serde_core", ] [[package]] @@ -753,19 +799,19 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.100" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -784,17 +830,11 @@ dependencies = [ "serde_json", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -810,15 +850,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memoffset" @@ -876,11 +916,20 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" -version = "0.37.3" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "memchr", ] @@ -915,6 +964,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -931,33 +986,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.13.1", + "num-traits", + "rand 0.9.4", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax 0.8.11", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -976,13 +1056,23 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -992,7 +1082,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[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.5", ] [[package]] @@ -1004,6 +1104,24 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "regex-automata" version = "0.3.9" @@ -1017,13 +1135,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.10", + "regex-syntax 0.8.11", ] [[package]] @@ -1034,9 +1152,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "ring" @@ -1058,7 +1176,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -1071,7 +1189,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -1080,9 +1198,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -1095,18 +1213,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -1116,9 +1234,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] [[package]] name = "same-file" @@ -1136,7 +1266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand", + "rand 0.8.8", "secp256k1-sys", "serde", ] @@ -1157,7 +1287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" dependencies = [ "bitcoin-private", - "rand", + "rand 0.8.8", "secp256k1", "secp256k1-zkp-sys", "serde", @@ -1181,9 +1311,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1191,22 +1321,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] @@ -1269,7 +1399,7 @@ dependencies = [ "elements", "getrandom 0.2.17", "ghost-cell", - "hex-conservative", + "hex-conservative 0.2.2", "miniscript", "simplicity-sys", ] @@ -1286,10 +1416,11 @@ dependencies = [ [[package]] name = "simplicityhl" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361316795ec753230c421d964ab60940d20d5252c2d39272452528832c9e0ec5" +checksum = "ae77168fa3367d234b11bb3999cbd6e04d7180db0b9e11f83de259528e3b23d1" dependencies = [ + "ariadne", "base64 0.21.7", "chumsky", "clap", @@ -1297,6 +1428,7 @@ dependencies = [ "getrandom 0.2.17", "itertools", "miniscript", + "semver", "serde", "serde_json", "simplicity-lang", @@ -1321,7 +1453,7 @@ dependencies = [ "serde", "serde_json", "simplicityhl", - "syn", + "syn 2.0.119", "thiserror", "toml", "toml_edit", @@ -1333,7 +1465,7 @@ version = "0.0.10" dependencies = [ "smplx-build", "smplx-test", - "syn", + "syn 2.0.119", ] [[package]] @@ -1361,6 +1493,7 @@ dependencies = [ "elements-miniscript", "hex", "minreq 3.0.0", + "rand_core 0.9.5", "serde", "serde_json", "sha2", @@ -1373,6 +1506,8 @@ name = "smplx-std" version = "0.0.10" dependencies = [ "either", + "rand 0.9.4", + "rand_core 0.9.5", "serde", "simplicityhl", "smplx-macros", @@ -1386,21 +1521,23 @@ version = "0.0.10" dependencies = [ "electrsd", "proc-macro2", + "proptest", "quote", + "rand 0.9.4", "serde", "simplicityhl", "smplx-regtest", "smplx-sdk", - "syn", + "syn 2.0.119", "thiserror", "toml", ] [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -1423,9 +1560,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -1439,7 +1587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -1447,29 +1595,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.4", ] [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -1523,16 +1671,16 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -1547,6 +1695,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1569,10 +1723,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] -name = "unicode-xid" -version = "0.2.6" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "untrusted" @@ -1592,6 +1746,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -1610,27 +1773,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1641,9 +1795,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1651,65 +1805,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.0", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -1840,22 +1960,13 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" @@ -1863,102 +1974,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" +name = "yansi" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.0", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1969,6 +2007,6 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/fixtures/Simplex.toml b/fixtures/Simplex.toml index 4d76e49d..7cf3c1ac 100644 --- a/fixtures/Simplex.toml +++ b/fixtures/Simplex.toml @@ -9,7 +9,7 @@ merkle = { path = "deps/merkle" } base_math = { path = "deps/math" } -test_crate = { git = "https://github.com/LesterEvSe/test-simplex-crate", tag = "v0.1.0" } +std = { git = "https://github.com/BlockstreamResearch/simplicityhl-std.git", tag = "v0.1.0" } # [regtest] # mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" @@ -24,6 +24,12 @@ test_crate = { git = "https://github.com/LesterEvSe/test-simplex-crate", tag = " # bitcoins = 10_000_000 # verbosity = 0 +# [test.fuzz] +# cases = 10000 +# max_shrink_iters = 100 +# max_global_rejects = 100 +# max_local_rejects = 100 + # [test.esplora] # url = "" # network = "" diff --git a/fixtures/deps/SimplicityHL-e56599a78b05bfcc/Simplex.toml b/fixtures/deps/SimplicityHL-e56599a78b05bfcc/Simplex.toml deleted file mode 100644 index d05bb553..00000000 --- a/fixtures/deps/SimplicityHL-e56599a78b05bfcc/Simplex.toml +++ /dev/null @@ -1,31 +0,0 @@ -# DEFAULT CONFIG - -[build] -src_dir = "./examples" -# simf_files = ["*.simf"] -# out_dir = "./src/artifacts" - -# [dependencies] -# some_dep = { git = "", path = "" } - -# [regtest] -# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" -# bitcoins = 10_000_000 -# rpc_port = 18443 -# esplora_port = 3000 -# rpc_user = "user" -# rpc_password = "password" - -# [test] -# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" -# bitcoins = 10_000_000 -# verbosity = 0 - -# [test.esplora] -# url = "" -# network = "" - -# [test.rpc] -# url = "" -# username = "" -# password = "" diff --git a/fixtures/deps/SimplicityHL-e56599a78b05bfcc/examples/simple_op.simf b/fixtures/deps/SimplicityHL-e56599a78b05bfcc/examples/simple_op.simf deleted file mode 100644 index 28e3e2c3..00000000 --- a/fixtures/deps/SimplicityHL-e56599a78b05bfcc/examples/simple_op.simf +++ /dev/null @@ -1,3 +0,0 @@ -pub fn hash(x: u32, y: u32) -> u32 { - jet::xor_32(x, y) -} diff --git a/fixtures/deps/test-simplex-crate-ab11d3f69256bd69/Simplex.toml b/fixtures/deps/test-simplex-crate-ab11d3f69256bd69/Simplex.toml deleted file mode 100644 index 2955815b..00000000 --- a/fixtures/deps/test-simplex-crate-ab11d3f69256bd69/Simplex.toml +++ /dev/null @@ -1,30 +0,0 @@ -# DEFAULT CONFIG - -[build] -src_dir = "test" -# src_dir = "./simf" -# simf_files = ["*.simf"] -# out_dir = "./src/artifacts" - -[dependencies] -# std = { git = "BlockstreamResearch/simf-std@v0.0.1", path = "lib/simf-std/contracts" } -hl_compiler = { git = "https://github.com/LesterEvSe/SimplicityHL" } - -# ARGS -# std=lib/simf-std/contracts -# std:math=node_modules/simf-math/contracts - -# [regtest] -# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" - -# [test] -# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" - -# [test.esplora] -# url = "" -# network = "" - -# [test.rpc] -# url = "" -# username = "" -# password = "" diff --git a/fixtures/deps/test-simplex-crate-ab11d3f69256bd69/test/smth.simf b/fixtures/deps/test-simplex-crate-ab11d3f69256bd69/test/smth.simf deleted file mode 100644 index 0cc47995..00000000 --- a/fixtures/deps/test-simplex-crate-ab11d3f69256bd69/test/smth.simf +++ /dev/null @@ -1 +0,0 @@ -pub use hl_compiler::simple_op::hash; diff --git a/fixtures/simf/dummy_panic.simf b/fixtures/simf/dummy_panic.simf index f86e0b5b..8a709d5b 100644 --- a/fixtures/simf/dummy_panic.simf +++ b/fixtures/simf/dummy_panic.simf @@ -1,3 +1,3 @@ fn main() { assert!(false); -} \ No newline at end of file +} diff --git a/fixtures/simf/failure_test.simf b/fixtures/simf/failure_test.simf new file mode 100644 index 00000000..ea5e1861 --- /dev/null +++ b/fixtures/simf/failure_test.simf @@ -0,0 +1,13 @@ +fn not(bit: bool) -> bool { + ::into(jet::complement_1(::into(bit))) +} + +fn main() { + let failure_value: u256 = param::FAILURE_VALUE; + let cmp_value: u256 = witness::CMP_VALUE; + + assert!(not(jet::eq_256( + failure_value, + cmp_value + ))); +} \ No newline at end of file diff --git a/fixtures/simf/imports/multidep.simf b/fixtures/simf/imports/multidep.simf index b48c90f6..257d8d72 100644 --- a/fixtures/simf/imports/multidep.simf +++ b/fixtures/simf/imports/multidep.simf @@ -1,16 +1,16 @@ use merkle::build_root::{get_root, hash as and_hash}; -use test_crate::smth::hash as or_hash; +use std::lib::asserts::assert_eq_32; -pub fn get_block_value_hash(prev_hash: u32, tx1: u32, tx2: u32) -> u32 { +fn get_block_value_hash(prev_hash: u32, tx1: u32, tx2: u32) -> u32 { let root: u32 = get_root(tx1, tx2); - or_hash(prev_hash, root) + jet::xor_32(prev_hash, root) } fn main() { let block_val_hash: u32 = get_block_value_hash(param::PREV_HASH, 10, 20); - assert!(jet::eq_32(block_val_hash, 27)); + assert_eq_32(block_val_hash, 27); let first_value: u32 = witness::TX1; let second_value: u32 = 22; - assert!(jet::eq_32(and_hash(first_value, second_value), 6)); + assert_eq_32(and_hash(first_value, second_value), 6); } diff --git a/fixtures/simf/sig_message.simf b/fixtures/simf/sig_message.simf new file mode 100644 index 00000000..eaf373e5 --- /dev/null +++ b/fixtures/simf/sig_message.simf @@ -0,0 +1,37 @@ +fn checksig(msg: u256, sig: Signature) { + jet::bip_0340_verify((param::PUBLIC_KEY, msg), sig); +} + +/// sha256("SimplexFixture/SigMessage") +fn tag_hash() -> u256 { + 0x844d91d7104634bfb6d6ba46312aba0245d1d3ecd7738e3e5e9229404224971b +} + +/// sha256(tag_hash || tag_hash || sig_all_hash), the BIP-340 tagged hash. +fn tagged_message() -> u256 { + let ctx: Ctx8 = jet::sha_256_ctx_8_init(); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash()); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash()); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, jet::sig_all_hash()); + + jet::sha_256_ctx_8_finalize(ctx) +} + +/// sha256(sig_all_hash). Not a tagged hash and not the sighash itself, +/// so neither Sighash nor Tagged can produce it. +fn custom_message() -> u256 { + let ctx: Ctx8 = jet::sha_256_ctx_8_init(); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, jet::sig_all_hash()); + + jet::sha_256_ctx_8_finalize(ctx) +} + +fn main() { + match witness::SIGNATURE { + Left(plain: Signature) => checksig(jet::sig_all_hash(), plain), + Right(derived: Either) => match derived { + Left(tagged: Signature) => checksig(tagged_message(), tagged), + Right(custom: Signature) => checksig(custom_message(), custom), + }, + } +} diff --git a/fixtures/simf/simple_storage.simf b/fixtures/simf/simple_storage.simf new file mode 100644 index 00000000..cf2cbb35 --- /dev/null +++ b/fixtures/simf/simple_storage.simf @@ -0,0 +1,105 @@ +/* + * Simple Storage Program for Liquid + * + * Only the owner of the storage can modify the value. + * + * ==== IMPORTANT ==== + * + * Based on the following resources: + * https://github.com/ElementsProject/elements/blob/master/src/consensus/amount.h + * https://github.com/ElementsProject/rust-elements/blob/f6ffc7800df14b81c0f5ae1c94368a78b99612b9/src/blind.rs#L471 + * + * The maximum allowed amount is 2,100,000,000,000,000 + * (i.e., 21,000,000 × 10^8), which is approximately 51 bits. + */ + +fn checksig(pk: Pubkey, sig: Signature) { + let msg: u256 = jet::sig_all_hash(); + jet::bip_0340_verify((pk, msg), sig); +} + +fn ensure_current_index_eq(expected_index: u32){ + assert!(jet::eq_32(jet::current_index(), expected_index)); +} + +fn ensure_input_and_output_script_hash_eq(index: u32) { + assert!(jet::eq_256(unwrap(jet::input_script_hash(index)), unwrap(jet::output_script_hash(index)))); +} + +fn ensure_output_is_op_return(index: u32) { + match jet::output_null_datum(index, 0) { + Some(entry: Option>>) => (), + None => panic!(), + } +} + +fn get_output_explicit_asset_amount(index: u32) -> (u256, u64) { + let pair: (Asset1, Amount1) = unwrap(jet::output_amount(index)); + let (asset, amount): (Asset1, Amount1) = pair; + + let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset); + let amount: u64 = unwrap_right::<(u1, u256)>(amount); + (asset_bits, amount) +} + +fn get_input_explicit_asset_amount(index: u32) -> (u256, u64) { + let pair: (Asset1, Amount1) = unwrap(jet::input_amount(index)); + let (asset, amount): (Asset1, Amount1) = pair; + + let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset); + let amount: u64 = unwrap_right::<(u1, u256)>(amount); + (asset_bits, amount) +} + + +fn ensure_output_asset_with_amount_eq(index: u32, expected_bits: u256, expected_amount: u64) { + let (asset, amount): (u256, u64) = dbg!(get_output_explicit_asset_amount(index)); + assert!(jet::eq_256(asset, expected_bits)); + assert!(jet::eq_64(amount, expected_amount)); +} + +fn ensure_one_bit(bit: bool) { assert!(jet::eq_1(::into(bit), 1)); } +fn ensure_zero_bit(bit: bool) { assert!(jet::eq_1(::into(bit), 0)); } + +fn increment_by(index: u32, amount: u32) -> u32 { + let (carry, result): (bool, u32) = dbg!(jet::add_32(index, amount)); + ensure_zero_bit(carry); + result +} + +fn enforce_stage_checks(index: u32, new_value: u64) { + ensure_input_and_output_script_hash_eq(index); + + let (asset_bits, old_value): (u256, u64) = get_input_explicit_asset_amount(index); + assert!(jet::eq_256(asset_bits, param::SLOT_ID)); + + ensure_output_asset_with_amount_eq(index, param::SLOT_ID, new_value); + + match dbg!(jet::lt_64(new_value, old_value)) { + // burn + true => { + let burn_output_index: u32 = increment_by(index, 1); + + let (carry, amount_to_burn): (bool, u64) = jet::subtract_64(old_value, new_value); + ensure_zero_bit(carry); + + ensure_output_is_op_return(burn_output_index); + ensure_output_asset_with_amount_eq(burn_output_index, param::SLOT_ID, amount_to_burn); + }, + // mint + false => { + let reissuance_output_index: u32 = increment_by(index, 1); + ensure_input_and_output_script_hash_eq(reissuance_output_index); + }, + }; +} + +fn main() { + let index: u32 = 0; + let n1: u32 = dbg!(111); + enforce_stage_checks(index, witness::NEW_VALUE); + + let n2: u32 = dbg!(222); + checksig(param::USER, witness::USER_SIGNATURE); + let n3: u32 = dbg!(333); +} diff --git a/fixtures/tests/basic_test.rs b/fixtures/tests/basic_test.rs index 21c2e540..c5b0b521 100644 --- a/fixtures/tests/basic_test.rs +++ b/fixtures/tests/basic_test.rs @@ -12,7 +12,7 @@ fn get_p2pk(context: &simplex::TestContext) -> (P2pkProgram, Script) { public_key: signer.get_schnorr_public_key().serialize(), }; - let p2pk = P2pkProgram::new(&arguments); + let p2pk = P2pkProgram::new(arguments); let p2pk_script = p2pk.get_script_pubkey(context.get_network()); (p2pk, p2pk_script) @@ -43,7 +43,7 @@ fn spend_p2pk(context: &simplex::TestContext) -> anyhow::Result<()> { ft.add_program_input( PartialInput::new(p2pk_utxos[0].clone()), - ProgramInput::new(Box::new(p2pk.as_ref().clone()), Box::new(witness.clone())), + ProgramInput::new(Box::new(p2pk.as_ref().clone()), witness), RequiredSignature::Witness("SIGNATURE".to_string()), ); diff --git a/fixtures/tests/log_level.rs b/fixtures/tests/log_level.rs index c575e2c9..e680c71a 100644 --- a/fixtures/tests/log_level.rs +++ b/fixtures/tests/log_level.rs @@ -7,7 +7,7 @@ fn setup_dummy(context: &simplex::TestContext) -> (DummyPanicProgram, simplex::s let signer = context.get_default_signer(); let dummy = - DummyPanicProgram::new(&DummyPanicArguments::default()).with_taproot_pubkey(signer.get_schnorr_public_key()); + DummyPanicProgram::new(DummyPanicArguments::default()).with_taproot_pubkey(signer.get_schnorr_public_key()); let script = dummy.get_script_pubkey(context.get_network()); @@ -30,7 +30,7 @@ fn dummy_log_level(context: simplex::TestContext) -> anyhow::Result<()> { ft.add_program_input( PartialInput::new(utxos[0].clone()), - ProgramInput::new(Box::new(dummy.as_ref().clone()), Box::new(DummyPanicWitness::default())), + ProgramInput::new(Box::new(dummy.as_ref().clone()), DummyPanicWitness::default()), RequiredSignature::None, ); diff --git a/fixtures/tests/metadata_cmr.rs b/fixtures/tests/metadata_cmr.rs index 1e5ecfcf..9c45fbae 100644 --- a/fixtures/tests/metadata_cmr.rs +++ b/fixtures/tests/metadata_cmr.rs @@ -33,7 +33,7 @@ fn expected_cmr<'a>(metadata: &'a Value, source: &str) -> &'a str { macro_rules! assert_metadata_cmr { ($metadata:expr, $source:literal, $program:ty, $arguments:ty) => {{ - let program = <$program>::new(&<$arguments>::default()); + let program = <$program>::new(<$arguments>::default()); assert_eq!( to_hex(program.get_cmr()), diff --git a/fixtures/tests/multidep_test.rs b/fixtures/tests/multidep_test.rs index fe6bd860..56947db6 100644 --- a/fixtures/tests/multidep_test.rs +++ b/fixtures/tests/multidep_test.rs @@ -8,7 +8,7 @@ use simplex_fixtures::artifacts::imports::multidep::derived_multidep::{MultidepA fn get_multidep(context: &simplex::TestContext) -> (MultidepProgram, Script) { let arguments = MultidepArguments { prev_hash: 5 }; - let p2pk = MultidepProgram::new(&arguments); + let p2pk = MultidepProgram::new(arguments); let p2pk_script = p2pk.get_script_pubkey(context.get_network()); (p2pk, p2pk_script) @@ -39,7 +39,7 @@ fn spend_p2pk(context: &simplex::TestContext) -> anyhow::Result<()> { ft.add_program_input( PartialInput::new(utxos[0].clone()), - ProgramInput::new(Box::new(program.as_ref().clone()), Box::new(witness.clone())), + ProgramInput::new(Box::new(program.as_ref().clone()), witness), RequiredSignature::None, ); diff --git a/fixtures/tests/nested_sig.rs b/fixtures/tests/nested_sig.rs index dcfb54a7..fd2a208b 100644 --- a/fixtures/tests/nested_sig.rs +++ b/fixtures/tests/nested_sig.rs @@ -12,7 +12,7 @@ fn get_nested_sig(context: &simplex::TestContext) -> (NestedSigProgram, Script) public_key: signer.get_schnorr_public_key().serialize(), }; - let program = NestedSigProgram::new(&arguments); + let program = NestedSigProgram::new(arguments); let script = program.get_script_pubkey(context.get_network()); (program, script) @@ -44,7 +44,7 @@ fn spend_nested_sig( ft.add_program_input( PartialInput::new(utxos[0].clone()), - ProgramInput::new(Box::new(program.as_ref().clone()), Box::new(witness)), + ProgramInput::new(Box::new(program.as_ref().clone()), witness), RequiredSignature::witness_with_path("INHERIT_OR_NOT", sig_path), ); diff --git a/fixtures/tests/prop_testing.rs b/fixtures/tests/prop_testing.rs new file mode 100644 index 00000000..d3262d23 --- /dev/null +++ b/fixtures/tests/prop_testing.rs @@ -0,0 +1,177 @@ +mod failure_test_prop { + use simplex::fuzz; + use simplex::fuzz::builders::{FinalTransactionBuilder, ProgramTarget}; + use simplex::fuzz::core::FuzzContext; + use simplex::fuzz::engine::FuzzStrategyBuilder; + use simplex::fuzz::{FuzzEngineBuilder, FuzzError, ProgramCheck, ProgramExecResult}; + use simplex::program::ProgramError; + use simplex::simplicityhl::elements::Script; + use simplex::simplicityhl::elements::pset::PartiallySignedTransaction; + use simplex::simplicityhl::{Arguments, WitnessValues}; + use simplex::transaction::{FinalTransaction, PartialInput, RequiredSignature, UTXO}; + + use simplex_fixtures::artifacts::failure_test::FailureTestProgram; + use simplex_fixtures::artifacts::failure_test::derived_failure_test::{FailureTestArguments, FailureTestWitness}; + + struct FailureTestCheck; + + const FAILURE_PROGRAM_TARGET: ProgramTarget = ProgramTarget::Input(0); + + fn failure_transaction_builder() -> Result { + let mut transaction = FinalTransaction::new(); + transaction.add_input(PartialInput::new(UTXO::default()), RequiredSignature::None); + + FinalTransactionBuilder::new(transaction, [FAILURE_PROGRAM_TARGET]) + } + + impl ProgramCheck for FailureTestCheck { + fn call( + &self, + _ctx: &FuzzContext, + _tx: &PartiallySignedTransaction, + _arguments: &Arguments, + _witness: &WitnessValues, + _input_index: usize, + program_exec_result: ProgramExecResult, + ) -> Result<(), String> { + let args = FailureTestArguments::from_arguments(_arguments)?; + let witness = FailureTestWitness::from_witness(_witness)?; + if args.failure_value == witness.cmp_value { + return Err(format!( + "Failed contract, failure_value == cmp_value , {program_exec_result:?}" + )); + } + if program_exec_result.is_err() { + println!("error: {program_exec_result:?}"); + return Err(format!("Failed contract, error: {program_exec_result:?}")); + } + Ok(()) + } + } + + struct ScriptMismatchCheck; + + impl ProgramCheck for ScriptMismatchCheck { + fn call( + &self, + _ctx: &FuzzContext, + _tx: &PartiallySignedTransaction, + _arguments: &Arguments, + _witness: &WitnessValues, + _input_index: usize, + program_exec_result: ProgramExecResult, + ) -> Result<(), String> { + match program_exec_result { + Err(ProgramError::ScriptPubkeyMismatch { .. }) => Ok(()), + Err(error) => Err(format!("expected a script pubkey mismatch, got {error}")), + Ok(_) => Err("expected execution to fail after replacing the program script".to_string()), + } + } + } + + #[ignore] + #[test] + fn possible_interface_failure_program() -> anyhow::Result<()> { + let config = fuzz::proptest::test_runner::Config { + test_name: ::core::option::Option::Some(::core::concat!( + ::core::module_path!(), + "::", + ::core::stringify!(possible_interface_failure_program) + )), + source_file: Some(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/", + stringify!(possible_interface_failure_program), + ".txt" + )), + ..Default::default() + }; + + let fuzz_engine = + FuzzEngineBuilder::::new(config); + + let strategy_storage = FuzzStrategyBuilder::::new() + .with_random() + .build(); + let transaction_builder = failure_transaction_builder()?; + let runner = fuzz_engine.build(strategy_storage, transaction_builder); + runner.run_with_check(FailureTestCheck); + + Ok(()) + } + + #[test] + fn possible_interface_failure_program_with_pool() -> anyhow::Result<()> { + let result = std::panic::catch_unwind(|| { + let config = fuzz::proptest::test_runner::Config { + test_name: ::core::option::Option::Some(::core::concat!( + ::core::module_path!(), + "::", + ::core::stringify!(possible_interface_failure_program_with_pool) + )), + source_file: Some(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/", + stringify!(possible_interface_failure_program_with_pool), + ".txt" + )), + ..Default::default() + }; + + let fuzz_engine_builder = + FuzzEngineBuilder::::new(config); + + // TODO: Add additional strategies to builder to make proper proptest + let strategy_storage = FuzzStrategyBuilder::::new() + .with_random_pool() + .build(); + let transaction_builder = failure_transaction_builder().unwrap(); + let runner = fuzz_engine_builder.build(strategy_storage, transaction_builder); + runner.run_with_check(FailureTestCheck); + }); + assert!(result.is_err()); + + Ok(()) + } + + #[ignore] + #[test] + fn post_hook_can_replace_one_program_script() -> anyhow::Result<()> { + let config = fuzz::proptest::test_runner::Config { + test_name: Some(concat!( + module_path!(), + "::", + stringify!(post_hook_can_replace_one_program_script) + )), + ..Default::default() + }; + let fuzz_engine = + FuzzEngineBuilder::::new(config); + let strategy_storage = FuzzStrategyBuilder::::new() + .with_random() + .build(); + let transaction_builder = + failure_transaction_builder()?.with_post_hook(|transaction, _program_script, _arguments, _witness| { + FinalTransactionBuilder::set_program_script(transaction, FAILURE_PROGRAM_TARGET, &Script::new()) + }); + + fuzz_engine + .build(strategy_storage, transaction_builder) + .run_with_check(ScriptMismatchCheck); + + Ok(()) + } + + #[simplex::fuzz] + fn possible_interface_failure_program_with_interesting_values( + fuzz_engine_builder: FuzzEngineBuilder, + ) -> anyhow::Result<()> { + // TODO: Add additional strategies to builder to make proper proptest + let strategy_storage = FuzzStrategyBuilder::::new().build(); + let transaction_builder = failure_transaction_builder()?; + let runner = fuzz_engine_builder.build(strategy_storage, transaction_builder); + runner.run_with_check(FailureTestCheck); + + Ok(()) + } +} diff --git a/fixtures/tests/sig_message.rs b/fixtures/tests/sig_message.rs new file mode 100644 index 00000000..df544baa --- /dev/null +++ b/fixtures/tests/sig_message.rs @@ -0,0 +1,115 @@ +use std::sync::Arc; + +use simplex::constants::DUMMY_SIGNATURE; +use simplex::either::Either; +use simplex::simplicityhl::elements::Script; +use simplex::simplicityhl::simplicity::hashes::{Hash, sha256}; +use simplex::transaction::{FinalTransaction, PartialInput, ProgramInput, RequiredSignature, SigMessage}; + +use simplex_fixtures::artifacts::sig_message::SigMessageProgram; +use simplex_fixtures::artifacts::sig_message::derived_sig_message::{SigMessageArguments, SigMessageWitness}; + +/// Must match the tag hashed into `tag_hash()` in sig_message.simf. +const TAG: &str = "SimplexFixture/SigMessage"; + +fn get_sig_message(context: &simplex::TestContext) -> (SigMessageProgram, Script) { + let signer = context.get_default_signer(); + + let arguments = SigMessageArguments { + public_key: signer.get_schnorr_public_key().serialize(), + }; + + let program = SigMessageProgram::new(&arguments); + let script = program.get_script_pubkey(context.get_network()); + + (program, script) +} + +fn fund_sig_message(context: &simplex::TestContext) -> anyhow::Result<()> { + let signer = context.get_default_signer(); + let (_, script) = get_sig_message(context); + + let tx_receipt = signer.send(script, 50_000)?; + println!("Funded: {}", tx_receipt); + + Ok(()) +} + +fn spend_sig_message( + context: &simplex::TestContext, + witness: SigMessageWitness, + required_sig: RequiredSignature, +) -> anyhow::Result<()> { + let signer = context.get_default_signer(); + let provider = context.get_default_provider(); + + let (program, script) = get_sig_message(context); + + let utxos = provider.fetch_scripthash_utxos(&script)?; + + let mut ft = FinalTransaction::new(); + + ft.add_program_input( + PartialInput::new(utxos[0].clone()), + ProgramInput::new(Box::new(program.as_ref().clone()), Box::new(witness)), + required_sig, + ); + + let tx_receipt = signer.broadcast(&ft)?; + println!("Broadcast: {}", tx_receipt); + + Ok(()) +} + +/// The program checks the signature against `jet::sig_all_hash()` directly. +#[simplex::test] +fn test_sighash_message(context: simplex::TestContext) -> anyhow::Result<()> { + fund_sig_message(&context)?; + + let witness = SigMessageWitness { + signature: Either::Left(DUMMY_SIGNATURE), + }; + + spend_sig_message( + &context, + witness, + RequiredSignature::witness_with_path("SIGNATURE", ["Left"]), + ) +} + +/// The program checks the signature against `sha256(tag || tag || sig_all_hash)`. +#[simplex::test] +fn test_tagged_message(context: simplex::TestContext) -> anyhow::Result<()> { + fund_sig_message(&context)?; + + let witness = SigMessageWitness { + signature: Either::Right(Either::Left(DUMMY_SIGNATURE)), + }; + + spend_sig_message( + &context, + witness, + RequiredSignature::witness_tagged("SIGNATURE", ["Right", "Left"], TAG), + ) +} + +/// The program checks the signature against `sha256(sig_all_hash)`, which is neither +/// the sighash nor a tagged hash, so it can only be expressed as a closure. +#[simplex::test] +fn test_custom_message(context: simplex::TestContext) -> anyhow::Result<()> { + fund_sig_message(&context)?; + + let witness = SigMessageWitness { + signature: Either::Right(Either::Right(DUMMY_SIGNATURE)), + }; + + let message = SigMessage::Custom(Arc::new(|sighash: [u8; 32]| { + sha256::Hash::hash(&sighash).to_byte_array() + })); + + spend_sig_message( + &context, + witness, + RequiredSignature::witness_with_message("SIGNATURE", ["Right", "Right"], message), + ) +} diff --git a/fixtures/tests/tapleaf_test.rs b/fixtures/tests/tapleaf_test.rs index dd67bbe8..6e560d46 100644 --- a/fixtures/tests/tapleaf_test.rs +++ b/fixtures/tests/tapleaf_test.rs @@ -8,7 +8,7 @@ fn program_tapleaf_test(context: simplex::TestContext) -> anyhow::Result<()> { let signer = context.get_default_signer(); let provider = context.get_default_provider(); - let tapleaf_check = TapleafCheckProgram::new(&TapleafCheckArguments::default()); + let tapleaf_check = TapleafCheckProgram::new(TapleafCheckArguments::default()); let tapleaf_check_script = tapleaf_check.get_script_pubkey(context.get_network()); let tx_receipt = signer.send(tapleaf_check_script.clone(), 50)?; @@ -24,7 +24,7 @@ fn program_tapleaf_test(context: simplex::TestContext) -> anyhow::Result<()> { ft.add_program_input( PartialInput::new(tapleaf_check_utxo), - ProgramInput::new(Box::new(tapleaf_check.as_ref().clone()), Box::new(witness.clone())), + ProgramInput::new(Box::new(tapleaf_check.as_ref().clone()), witness), RequiredSignature::None, );