diff --git a/Cargo.lock b/Cargo.lock index abde28d0..462a5c33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,10 +129,12 @@ dependencies = [ "indexmap", "itertools", "miette", + "minicbor", "owo-colors", "pallas", "pallas-traverse", "petgraph", + "proptest", "pulldown-cmark", "rayon", "regex", @@ -149,10 +151,50 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.69" +name = "anstream" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "arrayvec" @@ -175,9 +217,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e80b5ad1afe82872b7aa3e9de9b206ecb85584aa324f0f60fa4c903ce935936b" +checksum = "c22fbe0413545c098358e56966ff22cdd039e10215ae213cfbd65032b119fc94" dependencies = [ "basic-toml", "mime", @@ -186,7 +228,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.13", ] [[package]] @@ -237,6 +279,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -299,18 +350,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bstr" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" dependencies = [ "memchr", "serde", @@ -391,42 +442,62 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", "strsim", - "termcolor", - "terminal_size 0.2.5", + "terminal_size 0.2.6", "unicase", "unicode-width", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "concolor-override" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" + +[[package]] +name = "concolor-query" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" dependencies = [ - "os_str_bytes", + "windows-sys 0.45.0", ] [[package]] @@ -453,15 +524,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] @@ -520,9 +591,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071c0f5945634bc9ba7a452f492377dd6b1993665ddb58f28704119b32f07a9a" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" dependencies = [ "generic-array", "rand_core", @@ -553,14 +624,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "der" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" dependencies = [ "const-oid", "zeroize", @@ -579,6 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -605,11 +677,12 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.0" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfcadd7eade8d8f960aa721e9731a50081694d3118c80eba744cbf68c7e5db" +checksum = "106401dadc137d05cb0d4ab4d42be089746aefdfe8992df4d0edcf351c16ddca" dependencies = [ "der", + "digest", "elliptic-curve", "rfc6979", "signature", @@ -623,9 +696,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b984fcbd8df0166b077ec083cbfe076fdffb6e2de92d966794fd060794b620d7" +checksum = "22cdacd4d6ed3f9b98680b679c0e52a823b8a2c7a97358d508fe247f2180c282" dependencies = [ "base16ct", "crypto-bigint", @@ -651,13 +724,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -756,9 +829,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -771,9 +844,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -781,15 +854,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -798,38 +871,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -845,9 +918,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -856,9 +929,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -1019,9 +1092,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -1083,9 +1156,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -1108,30 +1181,31 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1195,9 +1269,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libm" @@ -1207,9 +1281,9 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "lock_api" @@ -1272,16 +1346,17 @@ dependencies = [ [[package]] name = "miette" -version = "5.5.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd9b301defa984bbdbe112b4763e093ed191750a0d914a78c1106b2d0fe703" +checksum = "7abdc09c381c9336b9f2e9bd6067a9a5290d20e2d2e2296f275456121c33ae89" dependencies = [ - "atty", "backtrace", + "backtrace-ext", + "is-terminal", "miette-derive", "once_cell", "owo-colors", - "supports-color", + "supports-color 2.0.0", "supports-hyperlinks", "supports-unicode", "terminal_size 0.1.17", @@ -1292,20 +1367,20 @@ dependencies = [ [[package]] name = "miette-derive" -version = "5.5.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c2401ab7ac5282ca5c8b518a87635b1a93762b0b90b9990c509888eeccba29" +checksum = "8842972f23939443013dfd3720f46772b743e86f1a81d120d4b6fb090f87de1c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -1319,9 +1394,9 @@ dependencies = [ [[package]] name = "minicbor" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d319d47468f164e5138b1c629bdd82ea3da0784ed1d41a22f8e0bcef76c2ae52" +checksum = "d7005aaf257a59ff4de471a9d5538ec868a21586534fff7f85dd97d4043a6139" dependencies = [ "half", "minicbor-derive", @@ -1335,7 +1410,7 @@ checksum = "1154809406efdb7982841adb6311b3d095b46f78342dd646736122fe6b19e267" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1472,13 +1547,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -1508,12 +1583,6 @@ dependencies = [ "num-integer", ] -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "output_vt100" version = "0.1.3" @@ -1529,7 +1598,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" dependencies = [ - "supports-color", + "supports-color 1.3.1", ] [[package]] @@ -1663,7 +1732,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -1748,9 +1817,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", @@ -1792,35 +1861,11 @@ dependencies = [ "yansi", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -1880,9 +1925,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -1957,6 +2002,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -1964,15 +2018,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -1981,15 +2035,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -2034,22 +2088,22 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustix" -version = "0.36.9" +version = "0.37.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2125,9 +2179,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642a62736682fdd8c71da0eb273e453c8ac74e33b9fb310e22ba5b03ec7651ff" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ "cc", ] @@ -2157,29 +2211,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.153" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a382c72b4ba118526e187430bb4963cd6d55051ebf13d9b25574d379cc98d20" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.153" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef476a5790f0f6decbc66726b6e5d63680ed518283e64c7df415989d880954f" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "indexmap", "itoa", @@ -2189,13 +2243,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -2252,9 +2306,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest", "rand_core", @@ -2293,9 +2347,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" dependencies = [ "base64ct", "der", @@ -2345,7 +2399,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -2365,21 +2419,31 @@ dependencies = [ ] [[package]] -name = "supports-hyperlinks" -version = "1.2.0" +name = "supports-color" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" +checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354" dependencies = [ - "atty", + "is-terminal", + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b4806e0b03b9906e76b018a5d821ebf198c8e9dc0829ed3328eeeb5094aed60" +dependencies = [ + "is-terminal", ] [[package]] name = "supports-unicode" -version = "1.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2" +checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7" dependencies = [ - "atty", + "is-terminal", ] [[package]] @@ -2394,25 +2458,27 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.4.0" +name = "syn" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "tempfile" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ - "winapi-util", + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -2427,12 +2493,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" dependencies = [ "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2448,22 +2514,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -2509,14 +2575,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -2529,13 +2594,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -2564,9 +2629,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" dependencies = [ "serde", "serde_spanned", @@ -2585,9 +2650,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", "serde", @@ -2622,7 +2687,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2669,9 +2734,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -2762,9 +2827,9 @@ checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" [[package]] name = "utf8parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vcpkg" @@ -2816,12 +2881,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -2862,7 +2926,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -2896,7 +2960,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2954,13 +3018,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -2969,71 +3033,137 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] @@ -3055,9 +3185,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" @@ -3100,9 +3230,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index fabb31c5..a3b494ce 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -1,13 +1,10 @@ -use std::{cell::RefCell, collections::HashMap, ops::Deref, sync::Arc}; - -use uplc::{ast::Type as UplcType, builtins::DefaultFunction}; - +use self::{environment::Environment, pretty::Printer}; use crate::{ ast::{Constant, DefinitionLocation, ModuleKind, Span}, tipo::fields::FieldMap, }; - -use self::{environment::Environment, pretty::Printer}; +use std::{cell::RefCell, collections::HashMap, ops::Deref, sync::Arc}; +use uplc::{ast::Type as UplcType, builtins::DefaultFunction}; mod environment; pub mod error; diff --git a/crates/aiken-project/Cargo.toml b/crates/aiken-project/Cargo.toml index 993ebd7c..f972900a 100644 --- a/crates/aiken-project/Cargo.toml +++ b/crates/aiken-project/Cargo.toml @@ -20,6 +20,7 @@ ignore = "0.4.20" indexmap = "1.9.2" itertools = "0.10.5" miette = { version = "5.5.0", features = ["fancy"] } +minicbor = "0.19.1" owo-colors = { version = "3.5.0", features = ["supports-colors"] } pallas = "0.18.0" pallas-traverse = "0.18.0" @@ -37,3 +38,6 @@ toml = "0.7.2" uplc = { path = '../uplc', version = "0.0.29" } walkdir = "2.3.2" zip = "0.6.4" + +[dev-dependencies] +proptest = "1.1.0" diff --git a/crates/aiken-project/src/blueprint/definitions.rs b/crates/aiken-project/src/blueprint/definitions.rs index 91d5e582..98b7211f 100644 --- a/crates/aiken-project/src/blueprint/definitions.rs +++ b/crates/aiken-project/src/blueprint/definitions.rs @@ -1,6 +1,7 @@ use aiken_lang::tipo::{Type, TypeVar}; use serde::{ self, + de::{self, Deserialize, Deserializer, MapAccess, Visitor}, ser::{Serialize, SerializeStruct, Serializer}, }; use std::{ @@ -52,6 +53,12 @@ impl Definitions { self.inner.remove(reference.as_key()); } + /// Insert a new definition + pub fn insert(&mut self, reference: &Reference, schema: T) { + self.inner + .insert(reference.as_key().to_string(), Some(schema)); + } + /// Register a new definition only if it doesn't exist. This uses a strategy of /// mark-and-insert such that recursive definitions are only built once. pub fn register( @@ -94,14 +101,14 @@ impl Reference { } /// Turn a reference into a key suitable for lookup. - fn as_key(&self) -> &str { + pub(crate) fn as_key(&self) -> &str { self.inner.as_str() } /// Turn a reference into a valid JSON pointer. Note that the JSON pointer specification /// indicates that '/' must be escaped as '~1' in pointer addresses (as they are otherwise /// treated as path delimiter in pointers paths). - fn as_json_pointer(&self) -> String { + pub(crate) fn as_json_pointer(&self) -> String { format!("#/definitions/{}", self.as_key().replace('/', "~1")) } } @@ -181,3 +188,55 @@ impl Serialize for Reference { s.end() } } + +impl<'a> Deserialize<'a> for Reference { + fn deserialize>(deserializer: D) -> Result { + #[derive(serde::Deserialize)] + enum Field { + #[serde(rename = "$ref")] + Ref, + } + const FIELDS: &[&str] = &["$ref"]; + + struct ReferenceVisitor; + + impl<'a> Visitor<'a> for ReferenceVisitor { + type Value = Reference; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Reference") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'a>, + { + let mut inner = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Ref => { + if inner.is_some() { + return Err(de::Error::duplicate_field(FIELDS[0])); + } + inner = Some(map.next_value()?); + } + } + } + + let inner: String = inner.ok_or_else(|| de::Error::missing_field(FIELDS[0]))?; + + match inner.strip_prefix("#/definitions/") { + Some(suffix) => Ok(Reference { + inner: suffix.to_string(), + }), + None => Err(de::Error::custom( + "Invalid reference; only local JSON pointer to #/definitions are allowed.", + )), + } + } + } + + deserializer.deserialize_struct("Reference", FIELDS, ReferenceVisitor) + } +} diff --git a/crates/aiken-project/src/blueprint/error.rs b/crates/aiken-project/src/blueprint/error.rs index d440b18c..9aac7739 100644 --- a/crates/aiken-project/src/blueprint/error.rs +++ b/crates/aiken-project/src/blueprint/error.rs @@ -1,8 +1,13 @@ -use super::schema; +use super::{ + definitions::Reference, + schema::{self, Schema}, +}; use aiken_lang::ast::Span; use miette::{Diagnostic, NamedSource}; +use minicbor as cbor; use owo_colors::{OwoColorize, Stream::Stdout}; use std::fmt::Debug; +use uplc::ast::Constant; #[derive(Debug, thiserror::Error, Diagnostic)] pub enum Error { @@ -37,9 +42,61 @@ pub enum Error { )] #[diagnostic(code("aiken::blueprint::address::parameterized"))] #[diagnostic(help( - "I can only compute addresses of validators that are fully applied. For example, a {keyword_spend} validator must have exactly 3 arguments: a datum, a redeemer and a context. If it has more, they need to be provided beforehand and applied directly in the validator. Applying parameters change the validator's compiled code, and thus the address.\n\nThis is why I need you to apply parameters first.", - keyword_spend = "spend".if_supports_color(Stdout, |s| s.purple())))] + "I can only compute addresses of validators that are fully applied. For example, a {keyword_spend} validator must have exactly {spend_arity} arguments: a datum, a redeemer and a context. If it has more, they need to be provided beforehand and applied directly to the validator.\n\nApplying parameters change the validator's compiled code, and thus the address. This is why I need you to apply parameters first using the {blueprint_apply_command} command.", + keyword_spend = "spend".if_supports_color(Stdout, |s| s.yellow()), + spend_arity = "3".if_supports_color(Stdout, |s| s.yellow()), + blueprint_apply_command = "blueprint apply".if_supports_color(Stdout, |s| s.purple()), + ))] ParameterizedValidator { n: usize }, + + #[error("I stumble upon something else than a constant when I expected one.")] + #[diagnostic(code("aiken:blueprint::apply::malformed::argument"))] + #[diagnostic(help( + "Parameters applied to blueprints must be constant; they cannot be lambdas or delayed terms." + ))] + NonConstantParameter, + + #[error("I couldn't find a definition corresponding to a reference.")] + #[diagnostic(code("aiken::blueprint::apply::unknown::reference"))] + #[diagnostic(help( + "While resolving a schema definition, I stumbled upon an unknown reference:\n\n→ {reference}\n\nThis is unfortunate, but signals that either the reference is invalid or that the corresponding schema definition is missing. Double-check the blueprint for that reference or definition.", + reference = reference.as_json_pointer().if_supports_color(Stdout, |s| s.red()) + ))] + UnresolvedSchemaReference { reference: Reference }, + + #[error("I caught a parameter application that seems off.")] + #[diagnostic(code("aiken::blueprint::apply::mismatch"))] + #[diagnostic(help( + "When applying parameters to a validator, I control that the shape of the parameter you give me matches what is specified in the blueprint. Unfortunately, it didn't match in this case.\n\nI am looking at the following value:\n\n{term}\n\nbut failed to match it against the specified schema:\n\n{expected}\n\n\nNOTE: this may only represent part of a bigger whole as I am validating the parameter incrementally.", + expected = serde_json::to_string_pretty(&schema).unwrap().if_supports_color(Stdout, |s| s.green()), + term = { + let mut buf = vec![]; + match term { + Constant::Data(data) => { + cbor::encode(data, &mut buf).unwrap(); + cbor::display(&buf).to_string() + }, + _ => term.to_pretty() + } + }.if_supports_color(Stdout, |s| s.red()), + ))] + SchemaMismatch { schema: Schema, term: Constant }, + + #[error( + "I discovered a discrepancy of elements between a given tuple and its declared schema." + )] + #[diagnostic(code("aiken::blueprint::apply::tuple::mismatch"))] + #[diagnostic(help( + "When validating a list-like schema with multiple 'items' schemas, I try to match each element of the instance with each item schema (by their position). Hence, I expect to be as many items in the declared schema ({expected}) than there are items in the instance ({found}).", + expected = expected.if_supports_color(Stdout, |s| s.green()), + found = found.if_supports_color(Stdout, |s| s.red()), + ))] + TupleItemsMismatch { expected: usize, found: usize }, + + #[error("I failed to convert some input into a valid parameter")] + #[diagnostic(code("aiken::blueprint::parse::parameter"))] + #[diagnostic(help("{hint}"))] + MalformedParameter { hint: String }, } unsafe impl Send for Error {} diff --git a/crates/aiken-project/src/blueprint/mod.rs b/crates/aiken-project/src/blueprint/mod.rs index ae5334ae..168953c8 100644 --- a/crates/aiken-project/src/blueprint/mod.rs +++ b/crates/aiken-project/src/blueprint/mod.rs @@ -1,22 +1,23 @@ pub mod definitions; pub mod error; +pub mod parameter; pub mod schema; pub mod validator; use crate::{config::Config, module::CheckedModules}; use aiken_lang::gen_uplc::CodeGenerator; -use definitions::{Definitions, Reference}; +use definitions::Definitions; use error::Error; use schema::{Annotated, Schema}; use std::fmt::Debug; use validator::Validator; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)] -pub struct Blueprint { +pub struct Blueprint { pub preamble: Preamble, - pub validators: Vec>, + pub validators: Vec, #[serde(skip_serializing_if = "Definitions::is_empty", default)] - pub definitions: Definitions, + pub definitions: Definitions>, } #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)] @@ -48,7 +49,7 @@ pub enum LookupResult<'a, T> { Many, } -impl Blueprint> { +impl Blueprint { pub fn new( config: &Config, modules: &CheckedModules, @@ -82,12 +83,8 @@ impl Blueprint> { } } -impl Blueprint -where - R: Clone + Default, - S: Clone + Default, -{ - pub fn lookup(&self, title: Option<&String>) -> Option>> { +impl Blueprint { + pub fn lookup(&self, title: Option<&String>) -> Option> { let mut validator = None; for v in self.validators.iter() { @@ -112,7 +109,7 @@ where action: F, ) -> Result where - F: Fn(Validator) -> Result, + F: Fn(Validator) -> Result, { match self.lookup(title) { Some(LookupResult::One(validator)) => action(validator.to_owned()), @@ -146,13 +143,13 @@ impl From<&Config> for Preamble { mod test { use super::*; use aiken_lang::builtins; - use schema::{Data, Items, Schema}; + use schema::{Data, Declaration, Items, Schema}; use serde_json::{self, json}; use std::collections::HashMap; #[test] fn serialize_no_description() { - let blueprint: Blueprint> = Blueprint { + let blueprint = Blueprint { preamble: Preamble { title: "Foo".to_string(), description: None, @@ -179,7 +176,7 @@ mod test { #[test] fn serialize_with_description() { - let blueprint: Blueprint> = Blueprint { + let blueprint = Blueprint { preamble: Preamble { title: "Foo".to_string(), description: Some("Lorem ipsum".to_string()), @@ -222,12 +219,15 @@ mod test { &HashMap::new(), |_| Ok(Schema::Data(Data::Bytes).into()), )?; - Ok(Schema::Data(Data::List(Items::One(Box::new(ref_bytes)))).into()) + Ok( + Schema::Data(Data::List(Items::One(Declaration::Referenced(ref_bytes)))) + .into(), + ) }, ) .unwrap(); - let blueprint: Blueprint> = Blueprint { + let blueprint = Blueprint { preamble: Preamble { title: "Foo".to_string(), description: None, diff --git a/crates/aiken-project/src/blueprint/parameter.rs b/crates/aiken-project/src/blueprint/parameter.rs new file mode 100644 index 00000000..ef0633e6 --- /dev/null +++ b/crates/aiken-project/src/blueprint/parameter.rs @@ -0,0 +1,428 @@ +use super::{ + definitions::{Definitions, Reference}, + error::Error, + schema::{Annotated, Constructor, Data, Declaration, Items, Schema}, +}; +use std::{iter, ops::Deref}; +use uplc::{ + ast::{Constant, Data as UplcData, DeBruijn, Term}, + PlutusData, +}; + +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] +pub struct Parameter { + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + + pub schema: Reference, +} + +impl From for Parameter { + fn from(schema: Reference) -> Parameter { + Parameter { + title: None, + schema, + } + } +} + +impl Parameter { + pub fn validate( + &self, + definitions: &Definitions>, + term: &Term, + ) -> Result<(), Error> { + let schema = &definitions + .lookup(&self.schema) + .map(Ok) + .unwrap_or_else(|| { + Err(Error::UnresolvedSchemaReference { + reference: self.schema.clone(), + }) + })? + .annotated; + + if let Term::Constant(constant) = term { + validate_schema(schema, definitions, constant) + } else { + Err(Error::NonConstantParameter) + } + } +} + +fn mismatch(term: &Constant, schema: Schema) -> Error { + Error::SchemaMismatch { + schema, + term: term.clone(), + } +} + +fn validate_schema( + schema: &Schema, + definitions: &Definitions>, + term: &Constant, +) -> Result<(), Error> { + match schema { + Schema::Data(data) => validate_data(data, definitions, term), + + Schema::Unit => expect_unit(term), + + Schema::Integer => expect_integer(term), + + Schema::Bytes => expect_bytes(term), + + Schema::String => expect_string(term), + + Schema::Boolean => expect_boolean(term), + + Schema::Pair(left, right) => { + let (term_left, term_right) = expect_pair(term)?; + + let left = + left.schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: left.reference().unwrap().clone(), + })?; + validate_schema(left, definitions, &term_left)?; + + let right = + right + .schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: right.reference().unwrap().clone(), + })?; + validate_schema(right, definitions, &term_right)?; + + Ok(()) + } + + Schema::List(Items::One(item)) => { + let terms = expect_list(term)?; + + let item = + item.schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: item.reference().unwrap().clone(), + })?; + + for ref term in terms { + validate_schema(item, definitions, term)?; + } + + Ok(()) + } + + Schema::List(Items::Many(items)) => { + let terms = expect_list(term)?; + + let items = items + .iter() + .map(|item| { + item.schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: item.reference().unwrap().clone(), + }) + }) + .collect::, _>>()?; + + if terms.len() != items.len() { + return Err(Error::TupleItemsMismatch { + expected: items.len(), + found: terms.len(), + }); + } + + for (item, ref term) in iter::zip(items, terms) { + validate_schema(item, definitions, term)?; + } + + Ok(()) + } + } +} + +fn validate_data( + data: &Data, + definitions: &Definitions>, + term: &Constant, +) -> Result<(), Error> { + match data { + Data::Opaque => expect_data(term), + + Data::Integer => expect_data_integer(term), + + Data::Bytes => expect_data_bytes(term), + + Data::List(Items::One(item)) => { + let terms = expect_data_list(term)?; + + let item = + item.schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: item.reference().unwrap().clone(), + })?; + + for ref term in terms { + validate_data(item, definitions, term)?; + } + + Ok(()) + } + + Data::List(Items::Many(items)) => { + let terms = expect_data_list(term)?; + + let items = items + .iter() + .map(|item| { + item.schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: item.reference().unwrap().clone(), + }) + }) + .collect::, _>>()?; + + if terms.len() != items.len() { + return Err(Error::TupleItemsMismatch { + expected: items.len(), + found: terms.len(), + }); + } + + for (item, ref term) in iter::zip(items, terms) { + validate_data(item, definitions, term)?; + } + + Ok(()) + } + + Data::Map(keys, values) => { + let terms = expect_data_map(term)?; + + let keys = + keys.schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: keys.reference().unwrap().clone(), + })?; + + let values = + values + .schema(definitions) + .ok_or_else(|| Error::UnresolvedSchemaReference { + reference: values.reference().unwrap().clone(), + })?; + + for (ref k, ref v) in terms { + validate_data(keys, definitions, k)?; + validate_data(values, definitions, v)?; + } + + Ok(()) + } + + Data::AnyOf(constructors) => { + let constructors: Vec<(usize, Vec<&Data>)> = constructors + .iter() + .map(|constructor| { + constructor + .annotated + .fields + .iter() + .map(|field| { + field.annotated.schema(definitions).ok_or_else(|| { + Error::UnresolvedSchemaReference { + reference: field.annotated.reference().unwrap().clone(), + } + }) + }) + .collect::>() + .map(|fields| (constructor.annotated.index, fields)) + }) + .collect::>()?; + + for (index, fields_schema) in constructors.iter() { + if let Ok(fields) = expect_data_constr(term, *index) { + if fields_schema.len() != fields.len() { + panic!("fields length different"); + } + + for (instance, schema) in iter::zip(fields, fields_schema) { + validate_data(schema, definitions, &instance)?; + } + + return Ok(()); + } + } + + Err(mismatch( + term, + Schema::Data(Data::AnyOf( + constructors + .iter() + .map(|(index, fields)| { + Constructor { + index: *index, + fields: fields + .iter() + .map(|_| Declaration::Inline(Box::new(Data::Opaque)).into()) + .collect(), + } + .into() + }) + .collect(), + )), + )) + } + } +} + +fn expect_data(term: &Constant) -> Result<(), Error> { + if matches!(term, Constant::Data(..)) { + return Ok(()); + } + + Err(mismatch(term, Schema::Data(Data::Opaque))) +} + +fn expect_data_integer(term: &Constant) -> Result<(), Error> { + if let Constant::Data(data) = term { + if matches!(data, PlutusData::BigInt(..)) { + return Ok(()); + } + } + + Err(mismatch(term, Schema::Data(Data::Integer))) +} + +fn expect_data_bytes(term: &Constant) -> Result<(), Error> { + if let Constant::Data(data) = term { + if matches!(data, PlutusData::BoundedBytes(..)) { + return Ok(()); + } + } + + Err(mismatch(term, Schema::Data(Data::Bytes))) +} + +fn expect_data_list(term: &Constant) -> Result, Error> { + if let Constant::Data(PlutusData::Array(elems)) = term { + return Ok(elems + .iter() + .map(|elem| Constant::Data(elem.to_owned())) + .collect()); + } + + Err(mismatch( + term, + Schema::Data(Data::List(Items::One(Declaration::Inline(Box::new( + Data::Opaque, + ))))), + )) +} + +fn expect_data_map(term: &Constant) -> Result, Error> { + if let Constant::Data(PlutusData::Map(pairs)) = term { + return Ok(pairs + .iter() + .map(|(k, v)| (Constant::Data(k.to_owned()), Constant::Data(v.to_owned()))) + .collect()); + } + + Err(mismatch( + term, + Schema::Data(Data::Map( + Declaration::Inline(Box::new(Data::Opaque)), + Declaration::Inline(Box::new(Data::Opaque)), + )), + )) +} + +fn expect_data_constr(term: &Constant, index: usize) -> Result, Error> { + if let Constant::Data(PlutusData::Constr(constr)) = term { + if let PlutusData::Constr(expected) = UplcData::constr(index as u64, vec![]) { + if expected.tag == constr.tag && expected.any_constructor == constr.any_constructor { + return Ok(constr + .fields + .iter() + .map(|field| Constant::Data(field.to_owned())) + .collect()); + } + } + } + + Err(mismatch( + term, + Schema::Data(Data::AnyOf(vec![Constructor { + index, + fields: vec![], + } + .into()])), + )) +} + +fn expect_unit(term: &Constant) -> Result<(), Error> { + if matches!(term, Constant::Unit) { + return Ok(()); + } + + Err(mismatch(term, Schema::Unit)) +} + +fn expect_integer(term: &Constant) -> Result<(), Error> { + if matches!(term, Constant::Integer(..)) { + return Ok(()); + } + + Err(mismatch(term, Schema::Integer)) +} + +fn expect_bytes(term: &Constant) -> Result<(), Error> { + if matches!(term, Constant::ByteString(..)) { + return Ok(()); + } + + Err(mismatch(term, Schema::Bytes)) +} + +fn expect_string(term: &Constant) -> Result<(), Error> { + if matches!(term, Constant::String(..)) { + return Ok(()); + } + + Err(mismatch(term, Schema::String)) +} + +fn expect_boolean(term: &Constant) -> Result<(), Error> { + if matches!(term, Constant::Bool(..)) { + return Ok(()); + } + + Err(mismatch(term, Schema::Boolean)) +} + +fn expect_pair(term: &Constant) -> Result<(Constant, Constant), Error> { + if let Constant::ProtoPair(_, _, left, right) = term { + return Ok((left.deref().clone(), right.deref().clone())); + } + + Err(mismatch( + term, + Schema::Pair( + Declaration::Inline(Box::new(Schema::Data(Data::Opaque))), + Declaration::Inline(Box::new(Schema::Data(Data::Opaque))), + ), + )) +} + +fn expect_list(term: &Constant) -> Result, Error> { + if let Constant::ProtoList(_, elems) = term { + return Ok(elems.to_owned()); + } + + Err(mismatch( + term, + Schema::List(Items::One(Declaration::Inline(Box::new(Schema::Data( + Data::Opaque, + ))))), + )) +} diff --git a/crates/aiken-project/src/blueprint/schema.rs b/crates/aiken-project/src/blueprint/schema.rs index 6e3ef11f..41f5f2d0 100644 --- a/crates/aiken-project/src/blueprint/schema.rs +++ b/crates/aiken-project/src/blueprint/schema.rs @@ -8,10 +8,11 @@ use aiken_lang::{ use owo_colors::{OwoColorize, Stream::Stdout}; use serde::{ self, + de::{self, Deserialize, Deserializer, MapAccess, Visitor}, ser::{Serialize, SerializeStruct, Serializer}, }; -use std::ops::Deref; -use std::{collections::HashMap, sync::Arc}; +use serde_json as json; +use std::{collections::HashMap, fmt, ops::Deref, sync::Arc}; // NOTE: Can be anything BUT 0 pub const REDEEMER_DISCRIMINANT: usize = 1; @@ -26,6 +27,50 @@ pub struct Annotated { pub annotated: T, } +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum Declaration { + Referenced(Reference), + Inline(Box), +} + +impl<'a, T> Declaration { + pub fn reference(&'a self) -> Option<&'a Reference> { + match self { + Declaration::Referenced(reference) => Some(reference), + Declaration::Inline(..) => None, + } + } + + fn try_schema( + &'a self, + definitions: &'a Definitions>, + cast: fn(&'a Schema) -> Option<&'a T>, + ) -> Option<&'a T> { + match self { + Declaration::Inline(inner) => Some(inner.deref()), + Declaration::Referenced(reference) => definitions + .lookup(reference) + .and_then(|s| cast(&s.annotated)), + } + } +} + +impl<'a> Declaration { + pub fn schema(&'a self, definitions: &'a Definitions>) -> Option<&'a Data> { + self.try_schema(definitions, |s| match s { + Schema::Data(data) => Some(data), + _ => None, + }) + } +} + +impl<'a> Declaration { + pub fn schema(&'a self, definitions: &'a Definitions>) -> Option<&'a Schema> { + self.try_schema(definitions, Some) + } +} + /// A schema for low-level UPLC primitives. #[derive(Debug, PartialEq, Eq, Clone)] pub enum Schema { @@ -34,8 +79,8 @@ pub enum Schema { Integer, Bytes, String, - Pair(Data, Data), - List(Vec), + Pair(Declaration, Declaration), + List(Items), Data(Data), } @@ -44,24 +89,25 @@ pub enum Schema { pub enum Data { Integer, Bytes, - List(Items), - Map(Box, Box), + List(Items), + Map(Declaration, Declaration), AnyOf(Vec>), Opaque, } /// A structure that represents either one or many elements. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] pub enum Items { - One(Box), - Many(Vec), + One(Declaration), + Many(Vec>), } /// Captures a single UPLC constructor with its #[derive(Debug, PartialEq, Eq, Clone)] pub struct Constructor { pub index: usize, - pub fields: Vec>, + pub fields: Vec>>, } impl From for Annotated { @@ -90,7 +136,7 @@ impl Annotated { description: Some("A redeemer wrapped in an extra constructor to make multi-validator detection possible on-chain.".to_string()), annotated: Schema::Data(Data::AnyOf(vec![Constructor { index: REDEEMER_DISCRIMINANT, - fields: vec![schema.into()], + fields: vec![Declaration::Referenced(schema).into()], } .into()])), }) @@ -219,7 +265,7 @@ impl Annotated { description: Some("An optional value.".to_string()), annotated: Constructor { index: 0, - fields: vec![generic.into()], + fields: vec![Declaration::Referenced(generic).into()], }, }, Annotated { @@ -260,22 +306,15 @@ impl Annotated { } if xs.len() == 2 => { definitions.remove(&generic); Data::Map( - Box::new( - xs.first() - .expect("length (== 2) checked in pattern clause") - .to_owned(), - ), - Box::new( - xs.last() - .expect("length (== 2) checked in pattern clause") - .to_owned(), - ), + xs.first() + .expect("length (== 2) checked in pattern clause") + .to_owned(), + xs.last() + .expect("length (== 2) checked in pattern clause") + .to_owned(), ) } - _ => { - // let inner = schema.clone().into_data(type_info)?.annotated; - Data::List(Items::One(Box::new(generic))) - } + _ => Data::List(Items::One(Declaration::Referenced(generic))), }; Ok(Schema::Data(data).into()) @@ -320,6 +359,7 @@ impl Annotated { .iter() .map(|elem| { Annotated::do_from_type(elem, modules, type_parameters, definitions) + .map(Declaration::Referenced) }) .collect::, _>>() .map_err(|e| e.backtrack(type_info))?; @@ -398,7 +438,7 @@ impl Data { fields.push(Annotated { title: field.label.clone(), description: field.doc.clone().map(|s| s.trim().to_string()), - annotated: reference, + annotated: Declaration::Referenced(reference), }); } @@ -510,6 +550,245 @@ impl Serialize for Schema { } } +fn visit_schema<'a, V>(mut map: V) -> Result +where + V: MapAccess<'a>, +{ + #[derive(serde::Deserialize)] + #[serde(field_identifier, rename_all = "camelCase")] + enum Field { + DataType, + Items, + Keys, + Values, + Left, + Right, + AnyOf, + OneOf, + } + + let mut data_type: Option = None; + let mut items: Option = None; // defer items deserialization to later + let mut keys = None; + let mut left = None; + let mut right = None; + let mut values = None; + let mut any_of = None; + + while let Some(key) = map.next_key()? { + match key { + Field::DataType => { + if data_type.is_some() { + return Err(de::Error::duplicate_field("dataType")); + } + data_type = Some(map.next_value()?); + } + Field::Items => { + if items.is_some() { + return Err(de::Error::duplicate_field("items")); + } + items = Some(map.next_value()?); + } + Field::Keys => { + if keys.is_some() { + return Err(de::Error::duplicate_field("keys")); + } + keys = Some(map.next_value()?); + } + Field::Values => { + if values.is_some() { + return Err(de::Error::duplicate_field("values")); + } + values = Some(map.next_value()?); + } + Field::Left => { + if left.is_some() { + return Err(de::Error::duplicate_field("left")); + } + left = Some(map.next_value()?); + } + Field::Right => { + if right.is_some() { + return Err(de::Error::duplicate_field("right")); + } + right = Some(map.next_value()?); + } + Field::AnyOf => { + if any_of.is_some() { + return Err(de::Error::duplicate_field("anyOf/oneOf")); + } + any_of = Some(map.next_value()?); + } + Field::OneOf => { + if any_of.is_some() { + return Err(de::Error::duplicate_field("anyOf/oneOf")); + } + any_of = Some(map.next_value()?); + } + } + } + + let expect_data_items = || match &items { + Some(items) => serde_json::from_value::>(items.clone()) + .map_err(|e| de::Error::custom(e.to_string())), + None => Err(de::Error::missing_field("items")), + }; + + let expect_schema_items = || match &items { + Some(items) => serde_json::from_value::>(items.clone()) + .map_err(|e| de::Error::custom(e.to_string())), + None => Err(de::Error::missing_field("items")), + }; + + let expect_no_items = || { + if items.is_some() { + return Err(de::Error::custom( + "unexpected fields 'items' for non-list data-type", + )); + } + Ok(()) + }; + + let expect_no_keys = || { + if keys.is_some() { + return Err(de::Error::custom( + "unexpected fields 'keys' for non-map data-type", + )); + } + Ok(()) + }; + + let expect_no_values = || { + if values.is_some() { + return Err(de::Error::custom( + "unexpected fields 'values' for non-map data-type", + )); + } + Ok(()) + }; + + let expect_no_any_of = || { + if any_of.is_some() { + return Err(de::Error::custom( + "unexpected fields 'anyOf' or 'oneOf'; applicators must singletons", + )); + } + Ok(()) + }; + + let expect_no_left_or_right = || { + if left.is_some() || right.is_some() { + return Err(de::Error::custom( + "unexpected field(s) 'left' and/or 'right' for a non-pair data-type", + )); + } + Ok(()) + }; + + match data_type { + None => { + expect_no_items()?; + expect_no_keys()?; + expect_no_values()?; + expect_no_left_or_right()?; + match any_of { + None => Ok(Schema::Data(Data::Opaque)), + Some(constructors) => Ok(Schema::Data(Data::AnyOf(constructors))), + } + } + Some(data_type) if data_type == "list" => { + expect_no_keys()?; + expect_no_values()?; + expect_no_any_of()?; + expect_no_left_or_right()?; + let items = expect_data_items()?; + Ok(Schema::Data(Data::List(items))) + } + Some(data_type) if data_type == "#list" => { + expect_no_keys()?; + expect_no_values()?; + expect_no_any_of()?; + expect_no_left_or_right()?; + let items = expect_schema_items()?; + Ok(Schema::List(items)) + } + Some(data_type) if data_type == "map" => { + expect_no_items()?; + expect_no_any_of()?; + expect_no_left_or_right()?; + match (keys, values) { + (Some(keys), Some(values)) => Ok(Schema::Data(Data::Map(keys, values))), + (None, _) => Err(de::Error::missing_field("keys")), + (Some(..), None) => Err(de::Error::missing_field("values")), + } + } + Some(data_type) if data_type == "#pair" => { + expect_no_items()?; + expect_no_keys()?; + expect_no_values()?; + expect_no_any_of()?; + match (left, right) { + (Some(left), Some(right)) => Ok(Schema::Pair(left, right)), + (None, _) => Err(de::Error::missing_field("left")), + (Some(..), None) => Err(de::Error::missing_field("right")), + } + } + Some(data_type) => { + expect_no_items()?; + expect_no_keys()?; + expect_no_values()?; + expect_no_any_of()?; + expect_no_left_or_right()?; + if data_type == "bytes" { + Ok(Schema::Data(Data::Bytes)) + } else if data_type == "integer" { + Ok(Schema::Data(Data::Integer)) + } else if data_type == "#unit" { + Ok(Schema::Unit) + } else if data_type == "#integer" { + Ok(Schema::Integer) + } else if data_type == "#bytes" { + Ok(Schema::Bytes) + } else if data_type == "#boolean" { + Ok(Schema::Boolean) + } else if data_type == "#string" { + Ok(Schema::String) + } else { + Err(de::Error::custom("unknown data-type")) + } + } + } +} + +impl<'a> Deserialize<'a> for Schema { + fn deserialize>(deserializer: D) -> Result { + struct SchemaVisitor; + + impl<'a> Visitor<'a> for SchemaVisitor { + type Value = Schema; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Schema") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'a>, + { + visit_schema(&mut map) + } + } + + deserializer.deserialize_struct( + "Schema", + &[ + "dataType", "items", "keys", "values", "anyOf", "oneOf", "left", "right", + ], + SchemaVisitor, + ) + } +} + impl Serialize for Data { fn serialize(&self, serializer: S) -> Result { match self { @@ -527,13 +806,7 @@ impl Serialize for Data { s.serialize_field("dataType", "bytes")?; s.end() } - Data::List(Items::One(item)) => { - let mut s = serializer.serialize_struct("List", 2)?; - s.serialize_field("dataType", "list")?; - s.serialize_field("items", &item)?; - s.end() - } - Data::List(Items::Many(items)) => { + Data::List(items) => { let mut s = serializer.serialize_struct("List", 2)?; s.serialize_field("dataType", "list")?; s.serialize_field("items", &items)?; @@ -554,6 +827,38 @@ impl Serialize for Data { } } } + +impl<'a> Deserialize<'a> for Data { + fn deserialize>(deserializer: D) -> Result { + struct DataVisitor; + + impl<'a> Visitor<'a> for DataVisitor { + type Value = Data; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Data") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'a>, + { + let schema = visit_schema(&mut map)?; + match schema { + Schema::Data(data) => Ok(data), + _ => Err(de::Error::custom("not a valid 'data'")), + } + } + } + + deserializer.deserialize_struct( + "Data", + &["dataType", "items", "keys", "values", "anyOf", "oneOf"], + DataVisitor, + ) + } +} + impl Serialize for Constructor { fn serialize(&self, serializer: S) -> Result { let mut s = serializer.serialize_struct("Constructor", 3)?; @@ -564,6 +869,60 @@ impl Serialize for Constructor { } } +impl<'a> Deserialize<'a> for Constructor { + fn deserialize>(deserializer: D) -> Result { + #[derive(serde::Deserialize)] + #[serde(field_identifier, rename_all = "camelCase")] + enum Field { + Index, + Fields, + } + const FIELDS: &[&str] = &["index", "fields"]; + + struct ConstructorVisitor; + + impl<'a> Visitor<'a> for ConstructorVisitor { + type Value = Constructor; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Constructor") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'a>, + { + let mut index = None; + let mut fields = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Index => { + if index.is_some() { + return Err(de::Error::duplicate_field(FIELDS[0])); + } + index = Some(map.next_value()?); + } + Field::Fields => { + if fields.is_some() { + return Err(de::Error::duplicate_field(FIELDS[1])); + } + fields = Some(map.next_value()?); + } + } + } + + Ok(Constructor { + index: index.ok_or_else(|| de::Error::missing_field(FIELDS[0]))?, + fields: fields.ok_or_else(|| de::Error::missing_field(FIELDS[1]))?, + }) + } + } + + deserializer.deserialize_struct("Constructor", FIELDS, ConstructorVisitor) + } +} + #[derive(Debug, PartialEq, Clone, thiserror::Error)] #[error("{}", context)] pub struct Error { @@ -681,6 +1040,7 @@ Here's the types I followed and that led me to this problem: #[cfg(test)] pub mod test { use super::*; + use proptest::prelude::*; use serde_json::{self, json, Value}; pub fn assert_json(schema: &impl Serialize, expected: Value) { @@ -712,7 +1072,7 @@ pub mod test { #[test] fn serialize_data_list_1() { let ref_integer = Reference::new("Int"); - let schema = Schema::Data(Data::List(Items::One(Box::new(ref_integer)))); + let schema = Schema::Data(Data::List(Items::One(Declaration::Referenced(ref_integer)))); assert_json( &schema, json!({ @@ -727,7 +1087,9 @@ pub mod test { #[test] fn serialize_data_list_2() { let ref_list_integer = Reference::new("List$Int"); - let schema = Schema::Data(Data::List(Items::One(Box::new(ref_list_integer)))); + let schema = Schema::Data(Data::List(Items::One(Declaration::Referenced( + ref_list_integer, + )))); assert_json( &schema, json!({ @@ -741,9 +1103,9 @@ pub mod test { #[test] fn serialize_data_map_1() { - let ref_integer = Reference::new("Int"); - let ref_bytes = Reference::new("ByteArray"); - let schema = Schema::Data(Data::Map(Box::new(ref_integer), Box::new(ref_bytes))); + let ref_integer = Declaration::Referenced(Reference::new("Int")); + let ref_bytes = Declaration::Referenced(Reference::new("ByteArray")); + let schema = Schema::Data(Data::Map(ref_integer, ref_bytes)); assert_json( &schema, json!({ @@ -782,12 +1144,12 @@ pub mod test { let schema = Schema::Data(Data::AnyOf(vec![ Constructor { index: 0, - fields: vec![Reference::new("Int").into()], + fields: vec![Declaration::Referenced(Reference::new("Int")).into()], } .into(), Constructor { index: 1, - fields: vec![Reference::new("Bytes").into()], + fields: vec![Declaration::Referenced(Reference::new("Bytes")).into()], } .into(), ])); @@ -848,4 +1210,224 @@ pub mod test { }), ) } + + #[test] + fn deserialize_data_opaque() { + assert_eq!(Data::Opaque, serde_json::from_value(json!({})).unwrap()) + } + + #[test] + fn deserialize_data_integer() { + assert_eq!( + Data::Integer, + serde_json::from_value(json!({ + "dataType": "integer", + })) + .unwrap() + ) + } + + #[test] + fn deserialize_data_bytes() { + assert_eq!( + Data::Bytes, + serde_json::from_value(json!({ + "dataType": "bytes", + })) + .unwrap() + ) + } + + #[test] + fn deserialize_data_list_one() { + assert_eq!( + Data::List(Items::One(Declaration::Referenced(Reference::new("foo")))), + serde_json::from_value(json!({ + "dataType": "list", + "items": { "$ref": "#/definitions/foo" } + })) + .unwrap() + ) + } + + #[test] + fn deserialize_data_list_many() { + assert_eq!( + Data::List(Items::Many(vec![ + Declaration::Referenced(Reference::new("foo")), + Declaration::Referenced(Reference::new("bar")) + ])), + serde_json::from_value(json!({ + "dataType": "list", + "items": [ + { "$ref": "#/definitions/foo" }, + { "$ref": "#/definitions/bar" } + ], + })) + .unwrap() + ) + } + + #[test] + fn deserialize_data_map() { + assert_eq!( + Data::Map( + Declaration::Referenced(Reference::new("foo")), + Declaration::Referenced(Reference::new("bar")) + ), + serde_json::from_value(json!({ + "dataType": "map", + "keys": { "$ref": "#/definitions/foo" }, + "values": { "$ref": "#/definitions/bar" } + })) + .unwrap() + ) + } + + #[test] + fn deserialize_any_of() { + assert_eq!( + Data::AnyOf(vec![Constructor { + index: 0, + fields: vec![ + Declaration::Referenced(Reference::new("foo")).into(), + Declaration::Referenced(Reference::new("bar")).into() + ], + } + .into()]), + serde_json::from_value(json!({ + "anyOf": [{ + "index": 0, + "fields": [ + { + "$ref": "#/definitions/foo", + }, + { + "$ref": "#/definitions/bar", + } + ] + }] + })) + .unwrap() + ) + } + + #[test] + fn deserialize_one_of() { + assert_eq!( + Data::AnyOf(vec![Constructor { + index: 0, + fields: vec![ + Declaration::Referenced(Reference::new("foo")).into(), + Declaration::Referenced(Reference::new("bar")).into() + ], + } + .into()]), + serde_json::from_value(json!({ + "oneOf": [{ + "index": 0, + "fields": [ + { + "$ref": "#/definitions/foo", + }, + { + "$ref": "#/definitions/bar", + } + ] + }] + })) + .unwrap() + ) + } + + fn arbitrary_data() -> impl Strategy { + let leaf = prop_oneof![Just(Data::Opaque), Just(Data::Bytes), Just(Data::Integer)]; + + leaf.prop_recursive(3, 8, 3, |inner| { + let r = prop_oneof![ + ".*".prop_map(|s| Declaration::Referenced(Reference::new(&s))), + inner.prop_map(|s| Declaration::Inline(Box::new(s))) + ]; + let constructor = + (0..3usize, prop::collection::vec(r.clone(), 0..3)).prop_map(|(index, fields)| { + Constructor { + index, + fields: fields.into_iter().map(|f| f.into()).collect(), + } + .into() + }); + + prop_oneof![ + (r.clone(), r.clone()).prop_map(|(k, v)| Data::Map(k, v)), + r.clone().prop_map(|x| Data::List(Items::One(x))), + prop::collection::vec(r, 1..3).prop_map(|xs| Data::List(Items::Many(xs))), + prop::collection::vec(constructor, 1..3).prop_map(Data::AnyOf) + ] + }) + } + + fn arbitrary_schema() -> impl Strategy { + prop_compose! { + fn data_strategy()(data in arbitrary_data()) -> Schema { + Schema::Data(data) + } + } + + let leaf = prop_oneof![ + Just(Schema::Unit), + Just(Schema::Boolean), + Just(Schema::Bytes), + Just(Schema::Integer), + Just(Schema::String), + data_strategy(), + ]; + + leaf.prop_recursive(3, 8, 3, |inner| { + let r = prop_oneof![ + ".*".prop_map(|s| Declaration::Referenced(Reference::new(&s))), + inner.prop_map(|s| Declaration::Inline(Box::new(s))) + ]; + prop_oneof![ + (r.clone(), r.clone()).prop_map(|(l, r)| Schema::Pair(l, r)), + r.clone().prop_map(|x| Schema::List(Items::One(x))), + prop::collection::vec(r, 1..3).prop_map(|xs| Schema::List(Items::Many(xs))), + ] + }) + } + + proptest! { + #[test] + fn data_serialization_roundtrip(data in arbitrary_data()) { + let json = serde_json::to_value(data); + let pretty = json + .as_ref() + .map(|v| serde_json::to_string_pretty(v).unwrap()) + .unwrap_or_else(|_| "invalid".to_string()); + assert!( + matches!( + json.and_then(serde_json::from_value::), + Ok{..} + ), + "\ncounterexample: {pretty}\n", + ) + } + } + + proptest! { + #[test] + fn schema_serialization_roundtrip(schema in arbitrary_schema()) { + let json = serde_json::to_value(schema); + let pretty = json + .as_ref() + .map(|v| serde_json::to_string_pretty(v).unwrap()) + .unwrap_or_else(|_| "invalid".to_string()); + assert!( + matches!( + json.and_then(serde_json::from_value::), + Ok{..} + ), + "\ncounterexample: {pretty}\n", + ) + } + } } diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index b23b5c8e..1b46ff91 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -1,6 +1,7 @@ use super::{ - definitions::{Definitions, Reference}, + definitions::Definitions, error::Error, + parameter::Parameter, schema::{Annotated, Schema}, }; use crate::module::{CheckedModule, CheckedModules}; @@ -13,44 +14,36 @@ use serde; use uplc::ast::{DeBruijn, Program, Term}; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)] -pub struct Validator { +pub struct Validator { pub title: String, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub datum: Option>, + pub datum: Option, - pub redeemer: Argument, + pub redeemer: Parameter, #[serde(skip_serializing_if = "Vec::is_empty")] #[serde(default)] - pub parameters: Vec>, + pub parameters: Vec, #[serde(flatten)] pub program: Program, #[serde(skip_serializing_if = "Definitions::is_empty")] #[serde(default)] - pub definitions: Definitions, + pub definitions: Definitions>, } -#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] -pub struct Argument { - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, - - pub schema: T, -} - -impl Validator> { +impl Validator { pub fn from_checked_module( modules: &CheckedModules, generator: &mut CodeGenerator, module: &CheckedModule, def: &TypedValidator, - ) -> Vec>, Error>> { + ) -> Vec> { let program = generator.generate(def).try_into().unwrap(); let is_multi_validator = def.other_fun.is_some(); @@ -85,7 +78,7 @@ impl Validator> { params: &[TypedArg], func: &TypedFunction, is_multi_validator: bool, - ) -> Result>, Error> { + ) -> Result { let mut args = func.arguments.iter().rev(); let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next()); @@ -102,7 +95,7 @@ impl Validator> { .iter() .map(|param| { Annotated::from_type(modules.into(), ¶m.tipo, &mut definitions) - .map(|schema| Argument { + .map(|schema| Parameter { title: Some(param.arg_name.get_label()), schema, }) @@ -130,7 +123,7 @@ impl Validator> { ) }) .transpose()? - .map(|schema| Argument { + .map(|schema| Parameter { title: datum.map(|datum| datum.arg_name.get_label()), schema, }), @@ -143,7 +136,7 @@ impl Validator> { module.code.clone(), ), }) - .map(|schema| Argument { + .map(|schema| Parameter { title: Some(redeemer.arg_name.get_label()), schema: match datum { Some(..) if is_multi_validator => Annotated::as_wrapped_redeemer( @@ -160,16 +153,16 @@ impl Validator> { } } -impl Validator -where - S: Clone, - R: Clone, -{ - pub fn apply(self, arg: &Term) -> Result { +impl Validator { + pub fn apply( + self, + definitions: &Definitions>, + arg: &Term, + ) -> Result { match self.parameters.split_first() { None => Err(Error::NoParametersToApply), - Some((_, tail)) => { - // TODO: Ideally, we should control that the applied term matches its schema. + Some((head, tail)) => { + head.validate(definitions, arg)?; Ok(Self { program: self.program.apply_term(arg), parameters: tail.to_vec(), @@ -182,7 +175,14 @@ where #[cfg(test)] mod test { - use super::*; + use super::{ + super::{ + definitions::{Definitions, Reference}, + error::Error, + schema::{Annotated, Constructor, Data, Declaration, Items, Schema}, + }, + *, + }; use crate::{module::ParsedModule, PackageName}; use aiken_lang::{ self, @@ -197,6 +197,7 @@ mod test { use indexmap::IndexMap; use serde_json::{self, json}; use std::{collections::HashMap, path::PathBuf}; + use uplc::ast as uplc; // TODO: Possible refactor this out of the module and have it used by `Project`. The idea would // be to make this struct below the actual project, and wrap it in another metadata struct @@ -318,6 +319,69 @@ mod test { assert_json_eq!(serde_json::to_value(validator).unwrap(), expected); } + fn fixture_definitions() -> Definitions> { + let mut definitions = Definitions::new(); + + // #/definitions/Int + // + // { + // "dataType": "integer" + // } + definitions + .register::<_, Error>(&builtins::int(), &HashMap::new(), |_| { + Ok(Schema::Data(Data::Integer).into()) + }) + .unwrap(); + + // #/definitions/ByteArray + // + // { + // "dataType": "bytes" + // } + definitions + .register::<_, Error>(&builtins::byte_array(), &HashMap::new(), |_| { + Ok(Schema::Data(Data::Bytes).into()) + }) + .unwrap(); + + // #/definitions/Bool + // + // { + // "anyOf": [ + // { + // "dataType": "constructor", + // "index": 0, + // "fields": [] + // }, + // { + // "dataType": "constructor", + // "index": 1, + // "fields": [] + // }, + // ] + // } + definitions.insert( + &Reference::new("Bool"), + Schema::Data(Data::AnyOf(vec![ + // False + Constructor { + index: 0, + fields: vec![], + } + .into(), + // True + Constructor { + index: 1, + fields: vec![], + } + .into(), + ])) + .into(), + ); + + definitions + } + #[test] fn mint_basic() { assert_validator( @@ -1096,4 +1160,266 @@ mod test { }), ) } + + #[test] + fn validate_arguments_integer() { + let definitions = fixture_definitions(); + + let term = Term::data(uplc::Data::integer(42.into())); + + let param = Parameter { + title: None, + schema: Reference::new("Int"), + }; + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_bytestring() { + let definitions = fixture_definitions(); + + let term = Term::data(uplc::Data::bytestring(vec![102, 111, 111])); + + let param = Parameter { + title: None, + schema: Reference::new("ByteArray"), + }; + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_list_inline() { + let schema = Reference::new("List$Int"); + + // #/definitions/List$Int + // + // { + // "dataType": "list", + // "items": { "dataType": "integer" } + // } + let mut definitions = fixture_definitions(); + definitions.insert( + &schema, + Schema::Data(Data::List(Items::One(Declaration::Inline(Box::new( + Data::Integer, + ))))) + .into(), + ); + + let term = Term::data(uplc::Data::list(vec![ + uplc::Data::integer(42.into()), + uplc::Data::integer(14.into()), + ])); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_list_ref() { + let schema = Reference::new("List$ByteArray"); + + // #/definitions/List$ByteArray + // + // { + // "dataType": "list", + // "items": { "$ref": "#/definitions/ByteArray" } + // } + let mut definitions = fixture_definitions(); + definitions.insert( + &schema, + Schema::Data(Data::List(Items::One(Declaration::Referenced( + Reference::new("ByteArray"), + )))) + .into(), + ); + + let term = Term::data(uplc::Data::list(vec![uplc::Data::bytestring(vec![ + 102, 111, 111, + ])])); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_tuple() { + let schema = Reference::new("Tuple$Int_ByteArray"); + + // #/definitions/Tuple$Int_ByteArray + // + // { + // "dataType": "list", + // "items": [ + // { "$ref": "#/definitions/Int" } + // { "$ref": "#/definitions/ByteArray" } + // ] + // } + let mut definitions = fixture_definitions(); + definitions.insert( + &schema, + Schema::Data(Data::List(Items::Many(vec![ + Declaration::Referenced(Reference::new("Int")), + Declaration::Referenced(Reference::new("ByteArray")), + ]))) + .into(), + ); + + let term = Term::data(uplc::Data::list(vec![ + uplc::Data::integer(42.into()), + uplc::Data::bytestring(vec![102, 111, 111]), + ])); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_dict() { + let schema = Reference::new("Dict$ByteArray_Int"); + + // #/definitions/Dict$Int_ByteArray + // + // { + // "dataType": "map", + // "keys": { "dataType": "bytes" }, + // "values": { "dataType": "integer" } + // } + let mut definitions = fixture_definitions(); + definitions.insert( + &Reference::new("Dict$ByteArray_Int"), + Schema::Data(Data::Map( + Declaration::Inline(Box::new(Data::Bytes)), + Declaration::Inline(Box::new(Data::Integer)), + )) + .into(), + ); + + let term = Term::data(uplc::Data::map(vec![( + uplc::Data::bytestring(vec![102, 111, 111]), + uplc::Data::integer(42.into()), + )])); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_constr_nullary() { + let schema = Reference::new("Bool"); + + let definitions = fixture_definitions(); + + let term = Term::data(uplc::Data::constr(1, vec![])); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_constr_n_ary() { + let schema = Reference::new("Foo"); + + // #/definitions/Foo + // + // { + // "anyOf": [ + // { + // "dataType": "constructor", + // "index": 0, + // "fields": [{ + // "$ref": "#/definitions/Bool + // }] + // }, + // ] + // } + let mut definitions = fixture_definitions(); + definitions.insert( + &schema, + Schema::Data(Data::AnyOf(vec![Constructor { + index: 0, + fields: vec![Declaration::Referenced(Reference::new("Bool")).into()], + } + .into()])) + .into(), + ); + + let term = Term::data(uplc::Data::constr(0, vec![uplc::Data::constr(0, vec![])])); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } + + #[test] + fn validate_arguments_constr_recursive() { + let schema = Reference::new("LinkedList$Int"); + + // #/definitions/LinkedList$Int + // + // { + // "anyOf": [ + // { + // "dataType": "constructor", + // "index": 0, + // "fields": [] + // }, + // { + // "dataType": "constructor", + // "index": 1, + // "fields": [{ + // "$ref": "#/definitions/Int + // "$ref": "#/definitions/LinkedList$Int + // }] + // }, + // ] + // } + let mut definitions = fixture_definitions(); + definitions.insert( + &schema, + Schema::Data(Data::AnyOf(vec![ + // Empty + Constructor { + index: 0, + fields: vec![], + } + .into(), + // Node + Constructor { + index: 1, + fields: vec![ + Declaration::Referenced(Reference::new("Int")).into(), + Declaration::Referenced(Reference::new("LinkedList$Int")).into(), + ], + } + .into(), + ])) + .into(), + ); + + let term = Term::data(uplc::Data::constr( + 1, + vec![ + uplc::Data::integer(14.into()), + uplc::Data::constr( + 1, + vec![ + uplc::Data::integer(42.into()), + uplc::Data::constr(0, vec![]), + ], + ), + ], + )); + + let param: Parameter = schema.into(); + + assert!(matches!(param.validate(&definitions, &term), Ok { .. })) + } } diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 914e3b7c..28677b20 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -12,11 +12,7 @@ pub mod pretty; pub mod script; pub mod telemetry; -use crate::blueprint::{ - definitions::Reference, - schema::{Annotated, Schema}, - Blueprint, -}; +use crate::blueprint::Blueprint; use aiken_lang::{ ast::{Definition, Function, ModuleKind, Tracing, TypedDataType, TypedFunction}, builtins, @@ -218,10 +214,7 @@ where self.compile(options) } - pub fn dump_uplc( - &self, - blueprint: &Blueprint>, - ) -> Result<(), Error> { + pub fn dump_uplc(&self, blueprint: &Blueprint) -> Result<(), Error> { let dir = self.root.join("artifacts"); self.event_listener @@ -362,8 +355,7 @@ where // Read blueprint let blueprint = File::open(self.blueprint_path()) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; - let blueprint: Blueprint = - serde_json::from_reader(BufReader::new(blueprint))?; + let blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; // Calculate the address let when_too_many = @@ -386,12 +378,11 @@ where &self, title: Option<&String>, param: &Term, - ) -> Result, Error> { + ) -> Result { // Read blueprint let blueprint = File::open(self.blueprint_path()) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; - let mut blueprint: Blueprint = - serde_json::from_reader(BufReader::new(blueprint))?; + let mut blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; // Apply parameters let when_too_many = @@ -400,7 +391,9 @@ where let applied_validator = blueprint.with_validator(title, when_too_many, when_missing, |validator| { - validator.apply(param).map_err(|e| e.into()) + validator + .apply(&blueprint.definitions, param) + .map_err(|e| e.into()) })?; // Overwrite validator diff --git a/crates/aiken/src/cmd/blueprint/apply.rs b/crates/aiken/src/cmd/blueprint/apply.rs index d9d3ff2c..d98200f9 100644 --- a/crates/aiken/src/cmd/blueprint/apply.rs +++ b/crates/aiken/src/cmd/blueprint/apply.rs @@ -1,18 +1,22 @@ use crate::with_project; -use aiken_project::error::Error; -use miette::IntoDiagnostic; -use std::{fs, path::PathBuf}; -use uplc::{ - ast::{DeBruijn, Term}, - parser, -}; +use aiken_project::{blueprint, error::Error}; +use owo_colors::{OwoColorize, Stream::Stderr}; +use std::{fs, path::PathBuf, process, rc::Rc}; +use uplc::ast::{Constant, DeBruijn, Term}; /// Apply a parameter to a parameterized validator. #[derive(clap::Args)] pub struct Args { + /// The parameter, as a Plutus Data (CBOR, hex-encoded) + parameter: String, + /// Path to project directory: Option, + /// Output file. Optional, print on stdout when omitted. + #[clap(short, long)] + out: Option, + /// Name of the validator's module within the project. Optional if there's only one validator. #[clap(short, long)] module: Option, @@ -20,23 +24,58 @@ pub struct Args { /// Name of the validator within the module. Optional if there's only one validator. #[clap(short, long)] validator: Option, - - /// The parameter, using high-level UPLC-syntax - parameter: String, } pub fn exec( Args { + parameter, directory, + out, module, validator, - parameter, }: Args, ) -> miette::Result<()> { - let term: Term = parser::term(¶meter) - .into_diagnostic()? - .try_into() - .into_diagnostic()?; + eprintln!( + "{} inputs", + " Parsing" + .if_supports_color(Stderr, |s| s.purple()) + .if_supports_color(Stderr, |s| s.bold()), + ); + + let bytes = hex::decode(parameter) + .map_err::(|e| { + blueprint::error::Error::MalformedParameter { + hint: format!("Invalid hex-encoded string: {e}"), + } + .into() + }) + .unwrap_or_else(|e| { + println!(); + e.report(); + process::exit(1) + }); + + let data = uplc::plutus_data(&bytes) + .map_err::(|e| { + blueprint::error::Error::MalformedParameter { + hint: format!("Invalid Plutus data; malformed CBOR encoding: {e}"), + } + .into() + }) + .unwrap_or_else(|e| { + println!(); + e.report(); + process::exit(1) + }); + + let term: Term = Term::Constant(Rc::new(Constant::Data(data))); + + eprintln!( + "{} blueprint", + " Analyzing" + .if_supports_color(Stderr, |s| s.purple()) + .if_supports_color(Stderr, |s| s.bold()), + ); with_project(directory, |p| { let title = module.as_ref().map(|m| { @@ -51,16 +90,35 @@ pub fn exec( let title = title.as_ref().or(validator.as_ref()); + eprintln!( + "{} parameter", + " Applying" + .if_supports_color(Stderr, |s| s.purple()) + .if_supports_color(Stderr, |s| s.bold()), + ); + let blueprint = p.apply_parameter(title, &term)?; let json = serde_json::to_string_pretty(&blueprint).unwrap(); - fs::write(p.blueprint_path(), json).map_err(|error| { - Error::FileIo { + match out { + None => { + println!("\n{}\n", json); + Ok(()) + } + Some(ref path) => fs::write(path, json).map_err(|error| Error::FileIo { error, path: p.blueprint_path(), - } - .into() - }) + }), + }?; + + eprintln!( + "{}", + " Done" + .if_supports_color(Stderr, |s| s.purple()) + .if_supports_color(Stderr, |s| s.bold()), + ); + + Ok(()) }) } diff --git a/crates/aiken/src/cmd/blueprint/convert.rs b/crates/aiken/src/cmd/blueprint/convert.rs index 2adeb3c5..d72a3183 100644 --- a/crates/aiken/src/cmd/blueprint/convert.rs +++ b/crates/aiken/src/cmd/blueprint/convert.rs @@ -65,7 +65,7 @@ pub fn exec( .map_err(|_| BlueprintError::InvalidOrMissingFile) .into_diagnostic()?; - let blueprint: Blueprint = + let blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint)).into_diagnostic()?; // Perform the conversion diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 884527a6..61d5e52c 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -1,23 +1,3 @@ -use std::{ - fmt::{self, Display}, - hash::{self, Hash}, - rc::Rc, -}; - -use num_bigint::BigInt; -use serde::{ - self, - de::{self, Deserialize, Deserializer, MapAccess, Visitor}, - ser::{Serialize, SerializeStruct, Serializer}, -}; - -use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart}; -use pallas_primitives::{ - alonzo::PlutusData, - babbage::{self as cardano, Language}, -}; -use pallas_traverse::ComputeHash; - use crate::{ builtins::DefaultFunction, debruijn::{self, Converter}, @@ -28,6 +8,24 @@ use crate::{ Machine, }, }; +use num_bigint::BigInt; +use num_traits::ToPrimitive; +use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart}; +use pallas_primitives::{ + alonzo::{self as pallas, Constr, PlutusData}, + babbage::{self as cardano, Language}, +}; +use pallas_traverse::ComputeHash; +use serde::{ + self, + de::{self, Deserialize, Deserializer, MapAccess, Visitor}, + ser::{Serialize, SerializeStruct, Serializer}, +}; +use std::{ + fmt::{self, Display}, + hash::{self, Hash}, + rc::Rc, +}; /// This represents a program in Untyped Plutus Core. /// A program contains a version tuple and a term. @@ -239,6 +237,61 @@ pub enum Constant { Data(PlutusData), } +pub struct Data {} + +// TODO: See about moving these builders upstream to Pallas? +impl Data { + pub fn integer(i: BigInt) -> PlutusData { + match i.to_i64() { + Some(i) => PlutusData::BigInt(pallas::BigInt::Int(i.into())), + None => { + let (sign, bytes) = i.to_bytes_be(); + match sign { + num_bigint::Sign::Minus => { + PlutusData::BigInt(pallas::BigInt::BigNInt(bytes.into())) + } + _ => PlutusData::BigInt(pallas::BigInt::BigUInt(bytes.into())), + } + } + } + } + + pub fn bytestring(bytes: Vec) -> PlutusData { + PlutusData::BoundedBytes(bytes.into()) + } + + pub fn map(kvs: Vec<(PlutusData, PlutusData)>) -> PlutusData { + PlutusData::Map(kvs.into()) + } + + pub fn list(xs: Vec) -> PlutusData { + PlutusData::Array(xs) + } + + pub fn constr(ix: u64, fields: Vec) -> PlutusData { + // NOTE: see https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L139-L155 + if ix < 7 { + PlutusData::Constr(Constr { + tag: 121 + ix, + any_constructor: None, + fields, + }) + } else if ix < 128 { + PlutusData::Constr(Constr { + tag: 1280 + ix - 7, + any_constructor: None, + fields, + }) + } else { + PlutusData::Constr(Constr { + tag: 102, + any_constructor: Some(ix), + fields, + }) + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Type { Bool, diff --git a/crates/uplc/src/builder.rs b/crates/uplc/src/builder.rs index d30d8c4c..91d2a763 100644 --- a/crates/uplc/src/builder.rs +++ b/crates/uplc/src/builder.rs @@ -2,13 +2,14 @@ use crate::{ ast::{Constant, Name, Term, Type}, builtins::DefaultFunction, }; +use pallas_primitives::alonzo::PlutusData; pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer"; pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer"; pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; pub const EXPECT_ON_LIST: &str = "__expect_on_list"; -impl Term { +impl Term { pub fn apply(self, arg: Self) -> Self { Term::Apply { function: self.into(), @@ -16,13 +17,6 @@ impl Term { } } - pub fn lambda(self, parameter_name: impl ToString) -> Self { - Term::Lambda { - parameter_name: Name::text(parameter_name).into(), - body: self.into(), - } - } - pub fn force(self) -> Self { Term::Force(self.into()) } @@ -31,10 +25,6 @@ impl Term { Term::Delay(self.into()) } - pub fn var(name: impl ToString) -> Self { - Term::Var(Name::text(name).into()) - } - pub fn integer(i: num_bigint::BigInt) -> Self { Term::Constant(Constant::Integer(i).into()) } @@ -55,6 +45,10 @@ impl Term { Term::Constant(Constant::Unit.into()) } + pub fn data(d: PlutusData) -> Self { + Term::Constant(Constant::Data(d).into()) + } + pub fn empty_list() -> Self { Term::Constant(Constant::ProtoList(Type::Data, vec![]).into()) } @@ -204,7 +198,7 @@ impl Term { .force() } - pub fn trace(self, msg_term: Term) -> Self { + pub fn trace(self, msg_term: Self) -> Self { Term::Builtin(DefaultFunction::Trace) .force() .apply(msg_term) @@ -212,41 +206,34 @@ impl Term { .force() } - pub fn assert_on_list(self) -> Term { - self.lambda(EXPECT_ON_LIST.to_string()) - .apply( - Term::var(EXPECT_ON_LIST.to_string()).apply(Term::var(EXPECT_ON_LIST.to_string())), - ) - .lambda(EXPECT_ON_LIST.to_string()) - .apply( - Term::var("__list_to_check".to_string()) - .delayed_choose_list( - Term::unit(), - Term::var("__check_with".to_string()) - .apply( - Term::head_list().apply(Term::var("__list_to_check".to_string())), - ) - .choose_unit( - Term::var(EXPECT_ON_LIST.to_string()) - .apply(Term::var(EXPECT_ON_LIST.to_string())) - .apply( - Term::tail_list() - .apply(Term::var("__list_to_check".to_string())), - ) - .apply(Term::var("__check_with".to_string())), - ), - ) - .lambda("__check_with".to_string()) - .lambda("__list_to_check".to_string()) - .lambda(EXPECT_ON_LIST), - ) - } - - pub fn final_wrapper(self: Term) -> Term { + pub fn final_wrapper(self) -> Self { self.delayed_if_else(Term::unit(), Term::Error) } - pub fn constr_fields_exposer(self: Term) -> Term { + pub fn repeat_tail_list(self, repeat: usize) -> Self { + let mut term = self; + + for _ in 0..repeat { + term = Term::tail_list().apply(term); + } + + term + } +} + +impl Term { + pub fn lambda(self, parameter_name: impl ToString) -> Self { + Term::Lambda { + parameter_name: Name::text(parameter_name).into(), + body: self.into(), + } + } + + pub fn var(name: impl ToString) -> Self { + Term::Var(Name::text(name).into()) + } + + pub fn constr_fields_exposer(self) -> Self { self.lambda(CONSTR_FIELDS_EXPOSER.to_string()).apply( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("__constr_var".to_string()))) @@ -254,7 +241,7 @@ impl Term { ) } - pub fn constr_index_exposer(self: Term) -> Term { + pub fn constr_index_exposer(self) -> Self { self.lambda(CONSTR_INDEX_EXPOSER.to_string()).apply( Term::fst_pair() .apply(Term::unconstr_data().apply(Term::var("__constr_var".to_string()))) @@ -262,7 +249,7 @@ impl Term { ) } - pub fn constr_get_field(self: Term) -> Term { + pub fn constr_get_field(self) -> Self { self.lambda(CONSTR_GET_FIELD.to_string()) .apply( Term::var(CONSTR_GET_FIELD.to_string()) @@ -298,13 +285,33 @@ impl Term { ) } - pub fn repeat_tail_list(self: Term, repeat: usize) -> Term { - let mut term = self; - - for _ in 0..repeat { - term = Term::tail_list().apply(term); - } - - term + pub fn assert_on_list(self) -> Self { + self.lambda(EXPECT_ON_LIST.to_string()) + .apply( + Term::var(EXPECT_ON_LIST.to_string()).apply(Term::var(EXPECT_ON_LIST.to_string())), + ) + .lambda(EXPECT_ON_LIST.to_string()) + .apply( + Term::var("__list_to_check".to_string()) + .delayed_choose_list( + Term::unit(), + Term::var("__check_with".to_string()) + .apply( + Term::head_list().apply(Term::var("__list_to_check".to_string())), + ) + .choose_unit( + Term::var(EXPECT_ON_LIST.to_string()) + .apply(Term::var(EXPECT_ON_LIST.to_string())) + .apply( + Term::tail_list() + .apply(Term::var("__list_to_check".to_string())), + ) + .apply(Term::var("__check_with".to_string())), + ), + ) + .lambda("__check_with".to_string()) + .lambda("__list_to_check".to_string()) + .lambda(EXPECT_ON_LIST), + ) } }