diff --git a/Cargo.lock b/Cargo.lock index 5ac3ac39..7a8c2bfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "addr2line" version = "0.20.0" @@ -17,6 +27,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.3" @@ -31,13 +52,19 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "anstream" version = "0.3.2" @@ -79,9 +106,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys", @@ -93,6 +120,55 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -114,6 +190,18 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.2" @@ -126,6 +214,36 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -134,9 +252,31 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium 0.7.0", + "tap", + "wyz", +] [[package]] name = "block-buffer" @@ -156,12 +296,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] + [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "byteorder" version = "1.4.3" @@ -177,12 +332,66 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cc" version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ + "jobserver", "libc", ] @@ -195,6 +404,7 @@ dependencies = [ "curta", "digest 0.10.7", "env_logger", + "log", "num", "plonky2", "plonky2x", @@ -215,11 +425,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "num-traits", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" -version = "4.3.19" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", "clap_derive", @@ -228,9 +458,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstream", "anstyle", @@ -256,6 +486,62 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "coins-bip32" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" +dependencies = [ + "bincode", + "bs58", + "coins-core", + "digest 0.10.7", + "getrandom", + "hmac", + "k256", + "lazy_static", + "serde", + "sha2 0.10.7", + "thiserror", +] + +[[package]] +name = "coins-bip39" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" +dependencies = [ + "bitvec 0.17.4", + "coins-bip32", + "getrandom", + "hmac", + "once_cell", + "pbkdf2 0.12.2", + "rand", + "sha2 0.10.7", + "thiserror", +] + +[[package]] +name = "coins-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" +dependencies = [ + "base64 0.21.2", + "bech32", + "bs58", + "digest 0.10.7", + "generic-array", + "hex", + "ripemd", + "serde", + "serde_derive", + "sha2 0.10.7", + "sha3", + "thiserror", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -264,9 +550,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "const-oid" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "const-random" @@ -290,6 +576,12 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.9.3" @@ -315,6 +607,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -364,6 +665,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -374,6 +687,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curta" version = "0.1.0" @@ -427,6 +749,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + [[package]] name = "der" version = "0.7.8" @@ -443,6 +771,23 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.9.0" @@ -459,7 +804,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -491,6 +879,34 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ena" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.32" @@ -500,6 +916,24 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" +dependencies = [ + "base64 0.13.1", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -513,6 +947,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.2" @@ -534,6 +974,322 @@ dependencies = [ "libc", ] +[[package]] +name = "eth-keystore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" +dependencies = [ + "aes", + "ctr", + "digest 0.10.7", + "hex", + "hmac", + "pbkdf2 0.11.0", + "rand", + "scrypt", + "serde", + "serde_json", + "sha2 0.10.7", + "sha3", + "thiserror", + "uuid", +] + +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types 0.12.1", + "scale-info", + "uint", +] + +[[package]] +name = "ethers" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b4026b97da8281276744741fac7eb385da905f6093c583331fa2953fdd4253" +dependencies = [ + "ethers-addressbook", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", + "ethers-providers", + "ethers-signers", + "ethers-solc", +] + +[[package]] +name = "ethers-addressbook" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcb6ffefc230d8c42874c51b28dc11dbb8de50b27a8fdf92648439d6baa68dc" +dependencies = [ + "ethers-core", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-contract" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4719a44c3d37ab07c6dea99ab174068d8c35e441b60b6c20ce4e48357273e8" +dependencies = [ + "ethers-contract-abigen", + "ethers-contract-derive", + "ethers-core", + "ethers-providers", + "ethers-signers", + "futures-util", + "hex", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ethers-contract-abigen" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155ea1b84d169d231317ed86e307af6f2bed6b40dd17e5e94bc84da21cadb21c" +dependencies = [ + "Inflector", + "dunce", + "ethers-core", + "ethers-etherscan", + "eyre", + "hex", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "reqwest", + "serde", + "serde_json", + "syn 2.0.28", + "toml", + "walkdir", +] + +[[package]] +name = "ethers-contract-derive" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8567ff196c4a37c1a8c90ec73bda0ad2062e191e4f0a6dc4d943e2ec4830fc88" +dependencies = [ + "Inflector", + "ethers-contract-abigen", + "ethers-core", + "hex", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.28", +] + +[[package]] +name = "ethers-core" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ca2514feb98918a0a31de7e1983c29f2267ebf61b2dc5d4294f91e5b866623" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "elliptic-curve", + "ethabi", + "generic-array", + "hex", + "k256", + "num_enum", + "once_cell", + "open-fastrlp", + "rand", + "rlp", + "serde", + "serde_json", + "strum", + "syn 2.0.28", + "tempfile", + "thiserror", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "ethers-etherscan" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22b3a8269d3df0ed6364bc05b4735b95f4bf830ce3aef87d5e760fb0e93e5b91" +dependencies = [ + "ethers-core", + "reqwest", + "semver", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "ethers-middleware" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c339aad74ae5c451d27e0e49c7a3c7d22620b119b4f9291d7aa21f72d7f366" +dependencies = [ + "async-trait", + "auto_impl", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "ethers-signers", + "futures-channel", + "futures-locks", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "ethers-providers" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b411b119f1cf0efb69e2190883dee731251882bb21270f893ee9513b3a697c48" +dependencies = [ + "async-trait", + "auto_impl", + "base64 0.21.2", + "bytes", + "enr", + "ethers-core", + "futures-core", + "futures-timer", + "futures-util", + "hashers", + "hex", + "http", + "instant", + "once_cell", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "ws_stream_wasm", +] + +[[package]] +name = "ethers-signers" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4864d387456a9c09a1157fa10e1528b29d90f1d859443acf06a1b23365fb518c" +dependencies = [ + "async-trait", + "coins-bip32", + "coins-bip39", + "elliptic-curve", + "eth-keystore", + "ethers-core", + "hex", + "rand", + "sha2 0.10.7", + "thiserror", + "tracing", +] + +[[package]] +name = "ethers-solc" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6c2b9625a2c639d46625f88acc2092a3cb35786c37f7c2128b3ca20f639b3c" +dependencies = [ + "cfg-if", + "dunce", + "ethers-core", + "glob", + "hex", + "home", + "md-5", + "num_cpus", + "once_cell", + "path-slash", + "rayon", + "regex", + "semver", + "serde", + "serde_json", + "solang-parser", + "svm-rs", + "thiserror", + "tiny-keccak", + "tokio", + "tracing", + "walkdir", + "yansi", +] + [[package]] name = "eyre" version = "0.6.8" @@ -550,6 +1306,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.1.20" @@ -565,6 +1331,34 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flex-error" version = "0.4.4" @@ -605,6 +1399,22 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.28" @@ -613,6 +1423,7 @@ checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -635,12 +1446,44 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +[[package]] +name = "futures-locks" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" +dependencies = [ + "futures-channel", + "futures-task", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -653,17 +1496,41 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] + [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", ] [[package]] @@ -674,6 +1541,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -695,6 +1563,35 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "h2" version = "0.3.20" @@ -707,7 +1604,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -731,6 +1628,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hashers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +dependencies = [ + "fxhash", +] + [[package]] name = "heck" version = "0.4.1" @@ -749,6 +1655,24 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "http" version = "0.2.9" @@ -806,7 +1730,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -814,26 +1738,78 @@ dependencies = [ ] [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", + "rlp", ] [[package]] -name = "idna" +name = "impl-serde" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -852,6 +1828,34 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.8.0" @@ -893,6 +1897,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -902,16 +1915,67 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.7", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keccak-hash" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce2bd4c29270e724d3eaadf7bdc8700af4221fc0ed771b855eadcd1b98d52851" dependencies = [ - "primitive-types", + "primitive-types 0.10.1", + "tiny-keccak", +] + +[[package]] +name = "lalrpop" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +dependencies = [ + "ascii-canvas", + "bit-set", + "diff", + "ena", + "is-terminal", + "itertools 0.10.5", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax", + "string_cache", + "term", "tiny-keccak", + "unicode-xid", ] +[[package]] +name = "lalrpop-util" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" + [[package]] name = "lazy_static" version = "1.4.0" @@ -946,6 +2010,15 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1005,6 +2078,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + [[package]] name = "num" version = "0.4.1" @@ -1104,6 +2183,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "object" version = "0.31.1" @@ -1120,95 +2220,276 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", + "ethereum-types", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "openssl" +version = "0.10.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +dependencies = [ + "arrayvec", + "bitvec 1.0.1", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "path-slash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", + "hmac", + "password-hash", + "sha2 0.10.7", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap 1.9.3", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version", +] + +[[package]] +name = "phf" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared 0.11.2", +] [[package]] -name = "openssl" -version = "0.10.56" +name = "phf_generator" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "phf_shared 0.11.2", + "rand", ] [[package]] -name = "openssl-macros" -version = "0.1.1" +name = "phf_macros" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ + "phf_generator", + "phf_shared 0.11.2", "proc-macro2", "quote", "syn 2.0.28", ] [[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.91" +name = "phf_shared" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "siphasher", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "phf_shared" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "lock_api", - "parking_lot_core", + "siphasher", ] [[package]] -name = "parking_lot_core" -version = "0.9.8" +name = "pin-project" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", + "pin-project-internal", ] [[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" +name = "pin-project-internal" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] [[package]] name = "pin-project-lite" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -1241,7 +2522,7 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "plonky2" version = "0.1.4" -source = "git+https://github.com/mir-protocol/plonky2.git#c9eed2bbf935c8da540df11de53edd2a78ed3497" +source = "git+https://github.com/mir-protocol/plonky2.git#dc7e0aa79bf051c1e9a01f2c12651253b4fa214d" dependencies = [ "ahash", "anyhow", @@ -1296,12 +2577,14 @@ checksum = "5696e2e2a6bb5c48a6e33fb0dd4d20d0a9472784b709964f337f224e99bd6d06" [[package]] name = "plonky2x" version = "0.1.0" -source = "git+ssh://git@github.com/succinctlabs/sdk.git#655f7e4479831418977874af3a2ac6dfd67fb12c" +source = "git+ssh://git@github.com/succinctlabs/sdk.git#d9c89affab6a109650d6049c6ad91266c9230390" dependencies = [ "anyhow", "curta", "curve25519-dalek", "digest 0.10.7", + "ethers", + "eyre", "hex", "itertools 0.10.5", "log", @@ -1312,6 +2595,7 @@ dependencies = [ "serde_json", "sha2 0.10.7", "subtle-encoding", + "tokio", ] [[package]] @@ -1320,16 +2604,80 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +dependencies = [ + "proc-macro2", + "syn 2.0.28", +] + [[package]] name = "primitive-types" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" dependencies = [ - "fixed-hash", + "fixed-hash 0.7.0", + "uint", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", "uint", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[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 1.0.109", + "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-macro-hack" version = "0.5.20+deprecated" @@ -1386,6 +2734,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1438,6 +2798,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1447,6 +2816,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.9.3" @@ -1482,7 +2862,7 @@ version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -1491,6 +2871,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -1500,25 +2881,91 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.22.6", "winreg", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1530,23 +2977,112 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.7" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno", "libc", "linux-raw-sys", "windows-sys", ] +[[package]] +name = "rustls" +version = "0.21.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.3", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-info" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "schannel" version = "0.1.22" @@ -1562,6 +3098,42 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +dependencies = [ + "hmac", + "pbkdf2 0.11.0", + "salsa20", + "sha2 0.10.7", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -1590,6 +3162,21 @@ name = "semver" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" @@ -1642,6 +3229,15 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1654,6 +3250,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1678,6 +3285,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1692,6 +3309,16 @@ name = "signature" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" @@ -1718,6 +3345,36 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "solang-parser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c792fe9fae2a2f716846f214ca10d5a1e21133e0bf36cef34bcc4a852467b21" +dependencies = [ + "itertools 0.10.5", + "lalrpop", + "lalrpop-util", + "phf", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spki" version = "0.7.2" @@ -1734,12 +3391,47 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.28", +] + [[package]] name = "subtle" version = "2.5.0" @@ -1761,6 +3453,26 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" +[[package]] +name = "svm-rs" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a04fc4f5cd35c700153b233f5575ccb3237e0f941fa5049d9e98254d10bf2fe" +dependencies = [ + "fs2", + "hex", + "home", + "once_cell", + "reqwest", + "semver", + "serde", + "serde_json", + "sha2 0.10.7", + "thiserror", + "url", + "zip", +] + [[package]] name = "syn" version = "1.0.109" @@ -1783,6 +3495,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.7.1" @@ -1791,7 +3509,7 @@ checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys", ] @@ -1843,6 +3561,17 @@ dependencies = [ "time", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1852,6 +3581,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "time" version = "0.3.25" @@ -1905,11 +3654,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -1918,7 +3666,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", "windows-sys", ] @@ -1944,6 +3692,31 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" +dependencies = [ + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.23.1", +] + [[package]] name = "tokio-util" version = "0.7.8" @@ -1958,6 +3731,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -1972,9 +3779,21 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "tracing-core" version = "0.1.31" @@ -1984,12 +3803,43 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "try-lock" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tungstenite" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "sha1", + "thiserror", + "url", + "utf-8", + "webpki", +] + [[package]] name = "typenum" version = "1.16.0" @@ -2029,6 +3879,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "unroll" version = "0.1.5" @@ -2039,6 +3895,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.4.0" @@ -2050,12 +3912,28 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2068,6 +3946,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2159,6 +4047,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" +dependencies = [ + "rustls-webpki 0.100.1", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2256,6 +4172,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -2265,6 +4190,40 @@ dependencies = [ "winapi", ] +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version", + "send_wrapper 0.6.0", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zeroize" version = "1.6.0" @@ -2284,3 +4243,53 @@ dependencies = [ "quote", "syn 2.0.28", ] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2 0.11.0", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 471c718a..96f91903 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ clap = { version = "4.3.18", features = ["derive"] } curta = { git = "https://github.com/succinctlabs/curta.git" } digest = "0.10.7" env_logger = "0.10.0" +log = "0.4.19" num = "0.4.1" plonky2 = { git = "https://github.com/mir-protocol/plonky2.git" } plonky2x = { git = "ssh://git@github.com/succinctlabs/sdk.git" } diff --git a/src/fixtures.rs b/src/fixtures.rs deleted file mode 100644 index ea1517d4..00000000 --- a/src/fixtures.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod generate_tests; diff --git a/src/fixtures/12000/signed_block.json b/src/fixtures/12000/signed_block.json new file mode 100644 index 00000000..d92790ac --- /dev/null +++ b/src/fixtures/12000/signed_block.json @@ -0,0 +1,177 @@ +{ + "header": { + "version": { + "block": "11", + "app": "1" + }, + "chain_id": "mocha-3", + "height": "12000", + "time": "2023-07-06T01:04:12.917405868Z", + "last_block_id": { + "hash": "ACF633A4EF045B55EB0BC14163685179A5DD3D96D2EB0FBCB5FCF927F3B228D0", + "parts": { + "total": 1, + "hash": "09F7691B98F013C3AA0B8C6BB4FBB3AD8D87F5D45FD6E4C71F374FBD9FF88C59" + } + }, + "last_commit_hash": "EF9C26754ED01EB6EE00E8F6D3EC607FEB8A9B6C62C72DD7AB25B28788468957", + "data_hash": "3D96B7D238E7E0456F6AF8E7CDF0A67BD6CF9C2089ECB559C659DCAA1F880353", + "validators_hash": "9F93BDDD1C5146C012BD813690EBE8DE664B717DA2909626336778B482A3B252", + "next_validators_hash": "9F93BDDD1C5146C012BD813690EBE8DE664B717DA2909626336778B482A3B252", + "consensus_hash": "C0B6A634B72AE9687EA53B6D277A73ABA1386BA3CFC6D0F26963602F7F6FFCD6", + "app_hash": "AF878F332FB817F73CD330ABC71497056544375FC5F5C81BF7FBEF3CBF9C460E", + "last_results_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", + "evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", + "proposer_address": "D436BB0B9DD430C2AE98E7D3CA651402DFD645D8" + }, + "data": { + "txs": null + }, + "commit": { + "height": "12000", + "round": 0, + "block_id": { + "hash": "422727EC819F17268A2C610896CDC14D43E0B0EA461FF045A33E4328AA6A971D", + "parts": { + "total": 1, + "hash": "2B53466FF044706BCC4D73E5F329CB73BDCB4F346AC15E76F31871CFC8E6CB87" + } + }, + "signatures": [ + { + "block_id_flag": 2, + "validator_address": "81A24EE534DEFE1557A4C7C437E8E8FBC2F834E8", + "timestamp": "2023-07-06T01:04:26.821547433Z", + "signature": "XdmDv+5LekPvFQP7dzMY+tMwj5w2PRcdLqKTFVqWiPCN5hjMj0DFkXW65xqxA4PcstwoZKeQA3YboKprWFBsDg==" + }, + { + "block_id_flag": 2, + "validator_address": "D436BB0B9DD430C2AE98E7D3CA651402DFD645D8", + "timestamp": "2023-07-06T01:04:26.803976588Z", + "signature": "WenPxlM7am78cknk2xld1yBv8r4V6rH53h/FnmPc1VQcS8zefatK5ECVCk5QeSBVWZhgv/iRYfsk8/hhItH3Dw==" + }, + { + "block_id_flag": 2, + "validator_address": "762CBA617226A799D898F134DD12661C7F1129EB", + "timestamp": "2023-07-06T01:04:26.920882313Z", + "signature": "3ZkvOVqxjOROmuCnwv9s9P5edt/WRu3dmlAtORhirc3ztdr140QkoNQoEMm8/wx250Go50KdhRb6D1GonXS/Dg==" + }, + { + "block_id_flag": 2, + "validator_address": "6CFE40213A360DC04C4E960803C9E85DAFF9BC4F", + "timestamp": "2023-07-06T01:04:26.80051773Z", + "signature": "MSK+VNZjW5KdWh0MstUPChCpMKLiS1cuyqod12IxXEm1l3Wl8A5EC9C2mABBz4MVFxKfbtl8wdzkEvXV7DvfAQ==" + }, + { + "block_id_flag": 2, + "validator_address": "110F48CDE021C66B9B00441D0A6B24D358608607", + "timestamp": "2023-07-06T01:04:26.951918887Z", + "signature": "KVNv/tksTHFxvhYVrrMZQkilTue6vW9aCfBQJKiLYsFNl/W4Ol+OunCZlWvQO7WLClJF5NNxO6cCMXazaCJ9Dg==" + }, + { + "block_id_flag": 1, + "validator_address": "", + "timestamp": "1970-01-01T00:00:00Z", + "signature": "" + }, + { + "block_id_flag": 2, + "validator_address": "EE927F115AD4FD4BC131CA311D332ADDAC62CCAC", + "timestamp": "2023-07-06T01:04:26.840958664Z", + "signature": "cSAmQ8R/U4tVCRX3LOkhmwYXZV62mIcLg1JHIxyMgNAPMhTLuG0qbhg2/EYVEZCcxKqpo2VFVa2EApiFPWN+Bw==" + }, + { + "block_id_flag": 2, + "validator_address": "62D008BE0CA3627CFEA58260D744B38AE46E921F", + "timestamp": "2023-07-06T01:04:26.9185206Z", + "signature": "aGbz2KcGsj+Uud5OSvWpPxXhrNnDB8ualIR9SDJSuLlkY+TapDq23Ix7P/+gxSxs2jJxWTUmhfu29ZiACVbjDA==" + } + ] + }, + "validator_set": { + "validators": [ + { + "address": "81A24EE534DEFE1557A4C7C437E8E8FBC2F834E8", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "3iWuyTWxD2V7Q/qX5ajU5SO9sPmXJgXwsGTv97FwSLo=" + }, + "power": "100010", + "name": null + }, + { + "address": "D436BB0B9DD430C2AE98E7D3CA651402DFD645D8", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "jeatGlaaIj57sNreGUq7lIciEhDh+oFUv2VKEP5hWKY=" + }, + "power": "100010", + "name": null + }, + { + "address": "762CBA617226A799D898F134DD12661C7F1129EB", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "6bdjjKHELaN9colwYy/ad+xh3MUgOVq106ZFucK46LE=" + }, + "power": "100000", + "name": null + }, + { + "address": "6CFE40213A360DC04C4E960803C9E85DAFF9BC4F", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "AZNamtmqyMW3W2aznvSJT6TheVXrNB9ihuVNbh0vpfA=" + }, + "power": "702", + "name": null + }, + { + "address": "110F48CDE021C66B9B00441D0A6B24D358608607", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "eXFJj8jJ0TwvoB1sqiDIIFy4UywE95w2YDLPaaVHxrk=" + }, + "power": "328", + "name": null + }, + { + "address": "DB792648B6EC5AA758F1C7D041E6CA4FBA1DE45A", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "vWBFLn8FayIkgQXn/SmJYTcdoNkzLvZfqBaRv1Gy5QU=" + }, + "power": "301", + "name": null + }, + { + "address": "EE927F115AD4FD4BC131CA311D332ADDAC62CCAC", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "SfFXATPCbIeRUsb9RP+BfdYOH0v7H2aGbPGx1HlR1I0=" + }, + "power": "50", + "name": null + }, + { + "address": "62D008BE0CA3627CFEA58260D744B38AE46E921F", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "Lan+2S2twOYqQ/Dc9VDgc9ixyeRjGldLTbiWJWbINe8=" + }, + "power": "1", + "name": null + } + ], + "proposer": { + "address": "D436BB0B9DD430C2AE98E7D3CA651402DFD645D8", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "jeatGlaaIj57sNreGUq7lIciEhDh+oFUv2VKEP5hWKY=" + }, + "power": "100010", + "name": null + }, + "total_voting_power": "301402" + } +} \ No newline at end of file diff --git a/src/fixtures/generate_tests.rs b/src/generate_tests.rs similarity index 81% rename from src/fixtures/generate_tests.rs rename to src/generate_tests.rs index fe9b4d1a..302b521e 100644 --- a/src/fixtures/generate_tests.rs +++ b/src/generate_tests.rs @@ -1,16 +1,20 @@ -use crate::merkle::{SignedBlock, TempSignedBlock}; +use crate::utils::{SignedBlock, TempSignedBlock}; use rand::Rng; use reqwest::Error; use serde::{Deserialize, Serialize}; use sha2::Sha256; -use std::{fs::File, io::Write}; +use std::{ + fs::{self, File}, + io::Write, + path::Path, +}; use subtle_encoding::hex; use tendermint::{merkle::simple_hash_from_byte_vectors, validator::Set as ValidatorSet}; #[derive(Debug, Deserialize)] struct Response { - _jsonrpc: String, - _id: i32, + jsonrpc: String, + id: i32, result: TempSignedBlock, } @@ -53,13 +57,12 @@ pub fn generate_val_array(num_validators: usize) { ); } -pub async fn get_celestia_consensus_signatures() -> Result<(), Error> { +pub async fn create_new_fixture(block_number: usize) -> Result<(), Error> { // Read from https://rpc-t.celestia.nodestake.top/signed_block?height=131950 using // Serves latest block - let height = 11000; let mut url = "http://rpc.testnet.celestia.citizencosmos.space/signed_block?height=".to_string(); - url.push_str(height.to_string().as_str()); + url.push_str(block_number.to_string().as_str()); // Send a GET request and wait for the response @@ -81,16 +84,19 @@ pub async fn get_celestia_consensus_signatures() -> Result<(), Error> { ), }; - println!("here"); - // Write to JSON file let json = serde_json::to_string(&block).unwrap(); - let mut path = "src/fixtures/".to_string(); - path.push_str(height.to_string().as_str()); + let mut path = "./src/fixtures/".to_string(); + path.push_str(block_number.to_string().as_str()); path.push_str("/signed_block.json"); - println!("Path: {:?}", path); - let mut file = File::create(path).unwrap(); + + // Ensure the directory exists + if let Some(parent) = Path::new(&path).parent() { + fs::create_dir_all(parent).unwrap(); + } + + let mut file = File::create(&path).unwrap(); file.write_all(json.as_bytes()).unwrap(); Ok(()) diff --git a/src/inputs.rs b/src/inputs.rs index 7995f0bb..9531da17 100644 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -1,6 +1,7 @@ +use std::fs; + /// Source (tendermint-rs): https://github.com/informalsystems/tendermint-rs/blob/e930691a5639ef805c399743ac0ddbba0e9f53da/tendermint/src/merkle.rs#L32 -use crate::merkle::{generate_proofs_from_header, non_absent_vote, SignedBlock, TempSignedBlock}; -use subtle_encoding::hex; +use crate::utils::{generate_proofs_from_header, non_absent_vote, SignedBlock, TempSignedBlock}; use tendermint::crypto::ed25519::VerificationKey; use tendermint::Signature; use tendermint::{validator::Set as ValidatorSet, vote::SignedVote, vote::ValidatorIndex}; @@ -51,11 +52,17 @@ fn get_path_indices(index: u64, total: u64) -> Vec { path_indices } -pub fn generate_step_inputs() -> CelestiaBlockProof { +pub fn generate_step_inputs(block: usize) -> CelestiaBlockProof { // Generate test cases from Celestia block: + let mut file = String::new(); + file.push_str("./src/fixtures/"); + file.push_str(&block.to_string()); + file.push_str("/signed_block.json"); + + let file_content = fs::read_to_string(file.as_str()).expect("error reading file"); + let temp_block = Box::new(TempSignedBlock::from( - serde_json::from_str::(include_str!("./fixtures/11000/signed_block.json")) - .unwrap(), + serde_json::from_str::(&file_content).expect("failed to parse json"), )); // Cast to SignedBlock @@ -69,11 +76,6 @@ pub fn generate_step_inputs() -> CelestiaBlockProof { ), }); - println!( - "header hash: {:?}", - String::from_utf8(hex::encode(block.header.hash().as_bytes())) - ); - let mut validators = Vec::new(); // Signatures or dummy @@ -97,16 +99,6 @@ pub fn generate_step_inputs() -> CelestiaBlockProof { ); let sig = signed_vote.signature(); let val_bytes = validator.hash_bytes(); - println!( - "pubkey: {:?}", - String::from_utf8(hex::encode(validator.pub_key.ed25519().unwrap().as_bytes())) - ); - println!("voting_power: {:?}", validator.power()); - println!( - "val_bytes: {:?}", - String::from_utf8(hex::encode(&val_bytes)) - ); - println!("val_bytes len: {:?}", val_bytes.len()); validators.push(Validator { pubkey: validator.pub_key.ed25519().unwrap(), diff --git a/src/main.rs b/src/main.rs index 978158a4..bb70abab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ -pub mod fixtures; +pub mod generate_tests; pub mod inputs; -pub mod merkle; +pub mod signature; +pub mod step; pub mod utils; pub mod validator; - -use crate::fixtures::generate_tests; +pub mod voting; use clap::Parser; @@ -17,9 +17,17 @@ enum Function { validators: usize, }, /// Calls the get_celestia_consensus_signatures function - GetCelestiaConsensusSignatures, + CreateNewFixture { + /// The block number to create a new fixture for + #[clap(short, long)] + block: usize, + }, /// Generates step inputs - GenerateStepInputs, + GenerateStepInputs { + /// Number of validators to generate test cases for + #[clap(short, long)] + block: usize, + }, } #[derive(Parser, Debug)] @@ -39,14 +47,13 @@ async fn main() { println!("Number of validators: {}", validators); generate_tests::generate_val_array(validators); } - Function::GetCelestiaConsensusSignatures => { - generate_tests::get_celestia_consensus_signatures() + Function::CreateNewFixture { block } => { + generate_tests::create_new_fixture(block) .await - .expect("Failed to get Celestia consensus signatures"); + .expect("Failed to create new fixture"); } - Function::GenerateStepInputs => { - let _celestia_step_inputs = inputs::generate_step_inputs(); - // println!("celestia_step_inputs: {:?}", celestia_step_inputs); + Function::GenerateStepInputs { block } => { + let _ = inputs::generate_step_inputs(block); } } } diff --git a/src/merkle.rs b/src/merkle.rs deleted file mode 100644 index fa5493d8..00000000 --- a/src/merkle.rs +++ /dev/null @@ -1,629 +0,0 @@ -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::cell::RefCell; -use std::rc::Rc; -use subtle_encoding::hex; -/// Source (tendermint-rs): https://github.com/informalsystems/tendermint-rs/blob/e930691a5639ef805c399743ac0ddbba0e9f53da/tendermint/src/merkle.rs#L32 -use tendermint::{ - block::Header, - block::{Commit, CommitSig}, - merkle::{Hash, MerkleHash}, - validator::{Info, Set as ValidatorSet}, - vote::Power, - vote::{ValidatorIndex, Vote}, -}; -use tendermint_proto::{ - types::BlockId as RawBlockId, types::Data as RawData, - version::Consensus as RawConsensusVersion, Protobuf, -}; - -/// Compute leaf hashes for arbitrary byte vectors. -/// The leaves of the tree are the bytes of the given byte vectors in -/// the given order. -pub fn hash_all_leaves(byte_vecs: &[impl AsRef<[u8]>]) -> Vec -where - H: MerkleHash + Default, -{ - let mut _hasher = H::default(); - let hashed_leaves = byte_vecs - .iter() - .map(|b| leaf_hash::(b.as_ref())) - .collect(); - hashed_leaves -} - -// Note: Implementations of ValidatorSet and SignedBlock differ in tendermint-rs and comet-bft -// Note: Following PR needs to be merged in tendermint-rs to remove TempValidatorSet and TempSignedBlock: https://github.com/informalsystems/tendermint-rs/pull/1340 -/// Validator set contains a vector of validators -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TempValidatorSet { - pub validators: Vec, - pub proposer: Option, - pub total_voting_power: Option, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -#[non_exhaustive] -pub struct TempSignedBlock { - /// Block header - pub header: Header, - - /// Transaction data - pub data: RawData, - - /// Commit - pub commit: Commit, - - /// Validator set - pub validator_set: TempValidatorSet, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -#[non_exhaustive] -pub struct SignedBlock { - /// Block header - pub header: Header, - - /// Transaction data - pub data: RawData, - - /// Commit - pub commit: Commit, - - /// Validator set - pub validator_set: ValidatorSet, -} - -// Note: Matches the implementation in tendermint-rs, need to add PR to tendermint-rs to support proofs -// https://github.com/tendermint/tendermint/blob/35581cf54ec436b8c37fabb43fdaa3f48339a170/crypto/merkle/proof.go#L35-L236 -#[derive(Clone)] -pub struct Proof { - pub total: u64, - pub index: u64, - pub leaf_hash: Hash, - pub aunts: Vec, -} - -#[derive(Clone)] -pub struct ProofNode { - pub hash: Hash, - pub left: Option>>, - pub right: Option>>, - pub parent: Option>>, -} - -impl Proof { - fn new(total: u64, index: u64, leaf_hash: Hash, aunts: Vec) -> Self { - Proof { - total, - index, - leaf_hash, - aunts, - } - } - - fn compute_root_hash(&self) -> Option { - compute_hash_from_aunts(self.index, self.total, self.leaf_hash, self.aunts.clone()) - } - - pub fn verify(&self, root_hash: &Hash, leaf: &[u8]) -> Result<(), Box> { - let leaf_hash = leaf_hash::(leaf); - println!("leaf_hash: {:?}", String::from_utf8(hex::encode(leaf_hash))); - if self.leaf_hash != leaf_hash { - return Err(format!( - "invalid leaf hash: wanted {:?} got {:?}", - hex::encode(leaf_hash), - hex::encode(self.leaf_hash) - ) - .into()); - } - let computed_hash = self - .compute_root_hash() - .expect("failed to compute root hash"); - if computed_hash != *root_hash { - return Err(format!( - "invalid root hash: wanted {:?} got {:?}", - hex::encode(root_hash), - hex::encode(computed_hash) - ) - .into()); - } - Ok(()) - } -} - -impl ProofNode { - fn new( - hash: Hash, - parent: Option>>, - left: Option>>, - right: Option>>, - ) -> Self { - ProofNode { - hash, - parent, - left, - right, - } - } - - fn flatten_aunts(&self) -> Vec { - let mut inner_hashes = Vec::new(); - let mut current_node = Some(Rc::new(RefCell::new(self.clone()))); - - while let Some(node) = current_node { - // Separate this into two steps to avoid holding onto a borrow across loop iterations - let (left, right) = { - let node_borrowed = node.borrow(); - (node_borrowed.left.clone(), node_borrowed.right.clone()) - }; - - match (&left, &right) { - (Some(left_node), _) => inner_hashes.push(left_node.borrow().hash), - (_, Some(right_node)) => inner_hashes.push(right_node.borrow().hash), - _ => {} - } - - // Now update current_node - current_node = node.borrow().parent.clone(); - } - - inner_hashes - } -} - -fn compute_hash_from_aunts( - index: u64, - total: u64, - leaf_hash: Hash, - inner_hashes: Vec, -) -> Option { - if index >= total || total == 0 { - return None; - } - match total { - 0 => panic!("Cannot call compute_hash_from_aunts() with 0 total"), - 1 => { - if !inner_hashes.is_empty() { - return None; - } - Some(leaf_hash) - } - _ => { - if inner_hashes.is_empty() { - return None; - } - let num_left = get_split_point(total as usize) as u64; - if index < num_left { - let left_hash = compute_hash_from_aunts( - index, - num_left, - leaf_hash, - inner_hashes[..inner_hashes.len() - 1].to_vec(), - ); - match left_hash { - None => return None, - Some(hash) => { - return Some(inner_hash::( - hash, - inner_hashes[inner_hashes.len() - 1], - )) - } - } - } - let right_hash = compute_hash_from_aunts( - index - num_left, - total - num_left, - leaf_hash, - inner_hashes[..inner_hashes.len() - 1].to_vec(), - ); - match right_hash { - None => None, - Some(hash) => Some(inner_hash::( - inner_hashes[inner_hashes.len() - 1], - hash, - )), - } - } - } -} - -fn proofs_from_byte_slices(items: Vec>) -> (Hash, Vec) { - let (trails, root) = trails_from_byte_slices(items.clone()); - let root_hash = root.borrow().hash; - let mut proofs = Vec::new(); - - for (i, trail) in trails.into_iter().enumerate() { - proofs.push(Proof::new( - items.len() as u64, - i as u64, - trail.borrow().hash, - trail.borrow().flatten_aunts(), - )); - } - - (root_hash, proofs) -} - -// Create trail from byte slice to root -fn trails_from_byte_slices( - items: Vec>, -) -> (Vec>>, Rc>) { - match items.len() { - 0 => { - let node = ProofNode::new(empty_hash(), None, None, None); - (vec![], Rc::new(RefCell::new(node))) - } - 1 => { - let node = Rc::new(RefCell::new(ProofNode::new( - leaf_hash::(&items[0]), - None, - None, - None, - ))); - - (vec![Rc::clone(&node)], Rc::clone(&node)) - } - _ => { - let k = get_split_point(items.len()); - let (lefts, left_root) = trails_from_byte_slices(items[..k].to_vec()); - let (rights, right_root) = trails_from_byte_slices(items[k..].to_vec()); - - let root_hash = inner_hash::(left_root.borrow().hash, right_root.borrow().hash); - let root = Rc::new(RefCell::new(ProofNode::new(root_hash, None, None, None))); - - { - let mut left_root_borrowed = (*left_root).borrow_mut(); - left_root_borrowed.parent = Some(Rc::clone(&root)); - left_root_borrowed.right = Some(Rc::clone(&right_root)); - } - { - let mut right_root_borrowed = (*right_root).borrow_mut(); - right_root_borrowed.parent = Some(Rc::clone(&root)); - right_root_borrowed.left = Some(Rc::clone(&left_root)); - } - - let trails = [lefts, rights].concat(); - - (trails, root) - } - } -} - -pub fn get_split_point(length: usize) -> usize { - if length < 1 { - panic!("Trying to split a tree with size < 1") - } - let bitlen = (length as f64).log2() as usize; - let k = 1 << bitlen; - if k == length { - k >> 1 - } else { - k - } -} - -fn empty_hash() -> Hash { - Sha256::digest([]) - .to_vec() - .try_into() - .expect("slice with incorrect length") -} - -pub fn leaf_hash(leaf: &[u8]) -> Hash -where - H: MerkleHash + Default, -{ - let mut hasher = H::default(); - hasher.leaf_hash(leaf) -} - -pub fn inner_hash(left: Hash, right: Hash) -> Hash -where - H: MerkleHash + Default, -{ - let mut hasher = H::default(); - hasher.inner_hash(left, right) -} - -pub fn generate_proofs_from_header(h: &Header) -> (Hash, Vec) { - let fields_bytes = vec![ - Protobuf::::encode_vec(h.version), - h.chain_id.clone().encode_vec(), - h.height.encode_vec(), - h.time.encode_vec(), - Protobuf::::encode_vec(h.last_block_id.unwrap_or_default()), - h.last_commit_hash.unwrap_or_default().encode_vec(), - h.data_hash.unwrap_or_default().encode_vec(), - h.validators_hash.encode_vec(), - h.next_validators_hash.encode_vec(), - h.consensus_hash.encode_vec(), - h.app_hash.clone().encode_vec(), - h.last_results_hash.unwrap_or_default().encode_vec(), - h.evidence_hash.unwrap_or_default().encode_vec(), - h.proposer_address.encode_vec(), - ]; - - proofs_from_byte_slices(fields_bytes) -} - -// Gets the vote struct: https://github.com/informalsystems/tendermint-rs/blob/c2b5c9e01eab1c740598aa14375a7453f3bfa436/light-client-verifier/src/operations/voting_power.rs#L202-L238 -pub fn non_absent_vote( - commit_sig: &CommitSig, - validator_index: ValidatorIndex, - commit: &Commit, -) -> Option { - // Cast the raw commit sig to a commit sig - let (validator_address, timestamp, signature, block_id) = match commit_sig { - CommitSig::BlockIdFlagAbsent { .. } => return None, - CommitSig::BlockIdFlagCommit { - validator_address, - timestamp, - signature, - } => ( - validator_address, - timestamp, - signature, - Some(commit.block_id), - ), - CommitSig::BlockIdFlagNil { - validator_address, - timestamp, - signature, - } => (validator_address, timestamp, signature, None), - }; - - Some(Vote { - vote_type: tendermint::vote::Type::Precommit, - height: commit.height, - round: commit.round, - block_id, - timestamp: Some(*timestamp), - validator_address: *validator_address, - validator_index, - signature: signature.clone(), - extension: Default::default(), - extension_signature: None, - }) -} - -#[cfg(test)] -pub(crate) mod tests { - use sha2::Sha256; - use subtle_encoding::hex; - use tendermint_proto::{types::SimpleValidator as RawSimpleValidator, Protobuf}; - - use crate::merkle::{generate_proofs_from_header, TempSignedBlock}; - use tendermint::{ - merkle::simple_hash_from_byte_vectors, - validator::{Set as ValidatorSet, SimpleValidator}, - vote::{SignedVote, ValidatorIndex}, - }; - - use super::{inner_hash, leaf_hash, non_absent_vote, SignedBlock}; - - #[test] - fn test_validator_inclusion() { - // These are test cases generated from querying `cosmos-hub` - // for the validator set at height 0 for validator 0. - - // let root_hash = [125u8, 130, 148, 137, 132, 154, 188, 169, 153, 181, 72, 1, 150, 95, 7, 68, 137, 114, 181, 223, 226, 151, 52, 72, 170, 185, 171, 167, 154, 96, 187, 240]; - // Total: 180 - let leaf_root_hex = "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"; - let leaf_string = "L123456"; - - let leaf_root = &hex::decode(leaf_root_hex).unwrap(); - let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; - - let root = simple_hash_from_byte_vectors::(&leaf_tree); - assert_eq!(leaf_root, &root); - } - - #[test] - fn test_multiple_validator_inclusion() { - // These are test cases generated from generating a random set of validators with a byte length of 38. - - // Serde JSON - let leaf_root_hex = "5541a94a9cf19e568401a2eed59f4ac8118c945d37803632aad655c6ee4f3ed6"; - - // JSON string - let validators = vec![ - "de6ad0941095ada2a7996e6a888581928203b8b69e07ee254d289f5b9c9caea193c2ab01902d", - "92fbe0c52937d80c5ea643c7832620b84bfdf154ec7129b8b471a63a763f2fe955af1ac65fd3", - "e902f88b2371ff6243bf4b0ebe8f46205e00749dd4dad07b2ea34350a1f9ceedb7620ab913c2", - ]; - - // Process the JSON value - for validator in &validators { - println!("Validator: {}", validator); - } - - let bytes_vec: Vec> = validators.iter().map(|s| hex::decode(s).unwrap()).collect(); - - let leaf_root = &hex::decode(leaf_root_hex).unwrap(); - let leaf_tree: Vec> = bytes_vec; - - let root = simple_hash_from_byte_vectors::(&leaf_tree); - assert_eq!(leaf_root, &root); - } - - #[test] - fn test_generate_validator_hash_proof() { - // Generate test cases from Celestia block: - let temp_block = TempSignedBlock::from( - serde_json::from_str::(include_str!( - "./fixtures/signed_celestia_block.json" - )) - .unwrap(), - ); - - // Cast to SignedBlock - let block = SignedBlock { - header: temp_block.header, - data: temp_block.data, - commit: temp_block.commit, - validator_set: ValidatorSet::new( - temp_block.validator_set.validators, - temp_block.validator_set.proposer, - ), - }; - - for validator in block.validator_set.validators() { - println!("Validator: {:?}", validator); - let encoded_bytes = - Protobuf::::encode_vec(SimpleValidator::from(validator)); - println!( - "Encoded Validator (Hex): {:?}", - String::from_utf8(hex::encode(encoded_bytes)) - ); - } - - let validator_hash = block.validator_set.hash(); - - // Check that the computed hash and validators_hash match - assert_eq!(validator_hash, block.header.validators_hash); - } - - #[test] - fn test_verify_signatures() { - // Generate test cases from Celestia block: - let temp_block = Box::new(TempSignedBlock::from( - serde_json::from_str::(include_str!( - "./fixtures/signed_celestia_block.json" - )) - .unwrap(), - )); - - // Cast to SignedBlock - let block = Box::new(SignedBlock { - header: temp_block.header, - data: temp_block.data, - commit: temp_block.commit, - validator_set: ValidatorSet::new( - temp_block.validator_set.validators, - temp_block.validator_set.proposer, - ), - }); - - // Source: https://github.com/informalsystems/tendermint-rs/blob/c2b5c9e01eab1c740598aa14375a7453f3bfa436/light-client-verifier/src/operations/voting_power.rs#L139-L198 - // Verify each of the signatures of the non_absent_votes - // Verify signatures - let non_absent_votes = - block - .commit - .signatures - .iter() - .enumerate() - .filter_map(|(idx, signature)| { - ValidatorIndex::try_from(idx) - .ok() - .and_then(|validator_idx| { - non_absent_vote(signature, validator_idx, &block.commit) - .map(|vote| (signature, vote)) - }) - }); - - let mut min_sign_bytes_len = 1000000; - let mut max_sign_bytes_len = 0; - - for (_, vote) in non_absent_votes { - let validator = Box::new( - match block.validator_set.validator(vote.validator_address) { - Some(validator) => validator, - None => continue, // Cannot find matching validator, so we skip the vote - }, - ); - - // Cast the vote into a signedVote struct (which is used to get the signed bytes) - let signed_vote = Box::new( - SignedVote::from_vote(vote.clone(), block.header.chain_id.clone()) - .expect("missing signature"), - ); - - let _pub_key = validator.pub_key.ed25519().unwrap(); - - // Get the encoded signed vote bytes - // https://github.com/celestiaorg/celestia-core/blob/main/proto/tendermint/types/canonical.proto#L30-L37 - let sign_bytes = signed_vote.sign_bytes(); - - if sign_bytes.len() < min_sign_bytes_len { - min_sign_bytes_len = sign_bytes.len(); - } - if sign_bytes.len() > max_sign_bytes_len { - max_sign_bytes_len = sign_bytes.len(); - } - - // Similar to encoding the vote: https://github.com/informalsystems/tendermint-rs/blob/c2b5c9e01eab1c740598aa14375a7453f3bfa436/tendermint/src/vote.rs#L267-L271 - // let decoded_vote: CanonicalVote = Protobuf::::decode_length_delimited_vec(&sign_bytes).expect("failed to decode sign_bytes"); - - // Verify that the message signed is in fact the sign_bytes - validator - .verify_signature::( - &sign_bytes, - signed_vote.signature(), - ) - .expect("invalid signature"); - - // TODO: We can break out of the loop when we have enough voting power. - // See https://github.com/informalsystems/tendermint-rs/issues/235 - } - - let validator_hash = block.validator_set.hash(); - - // Check that the computed hash and validators_hash match - assert_eq!(validator_hash, block.header.validators_hash); - - let header_hash = block.header.hash(); - let header_hash_bytes = block.commit.block_id.hash; - assert_eq!(header_hash, header_hash_bytes); - } - - #[test] - fn test_verify_validator_hash_from_root_proof() { - // Generate test cases from Celestia block: - let block = tendermint::Block::from( - serde_json::from_str::(include_str!( - "./fixtures/celestia_block.json" - )) - .unwrap(), - ); - - let (root_hash, proofs) = generate_proofs_from_header(&block.header); - - let validator_hash_index = 7; - - // Verify validator proof - proofs[validator_hash_index] - .verify(&root_hash, &block.header.validators_hash.encode_vec()) - .unwrap(); - - // Verify proof using aunts - let mut path_indices = vec![]; - let mut path_values = vec![]; - for i in 0..proofs[validator_hash_index].aunts.len() { - path_values.push(proofs[validator_hash_index].aunts[i]); - } - - let mut current_total = proofs[validator_hash_index].total; - let mut current_index = proofs[validator_hash_index].index; - while current_total >= 1 { - path_indices.push(current_index % 2 == 1); - current_total = current_total / 2; - current_index = current_index / 2; - } - - let validators_hash = block.header.validators_hash.encode_vec(); - let leaf_hash = leaf_hash::(&validators_hash); - - let mut current_hash = leaf_hash; - for i in 0..path_indices.len() { - if path_indices[i] { - current_hash = inner_hash::(path_values[i], current_hash); - } else { - current_hash = inner_hash::(current_hash, path_values[i]); - } - } - - assert_eq!(current_hash, root_hash); - } -} diff --git a/src/signature.rs b/src/signature.rs new file mode 100644 index 00000000..d3ef2721 --- /dev/null +++ b/src/signature.rs @@ -0,0 +1,321 @@ +//! The protobuf encoding of a Tendermint validator is a deterministic function of the validator's +//! public key (32 bytes) and voting power (int64). The encoding is as follows in bytes: +// +//! 10 34 10 32 16 +// +//! The `pubkey` is encoded as the raw list of bytes used in the public key. The `varint` is +//! encoded using protobuf's default integer encoding, which consist of 7 bit payloads. You can +//! read more about them here: https://protobuf.dev/programming-guides/encoding/#varints. +use curta::plonky2::field::CubicParameters; +use plonky2::field::extension::Extendable; +use plonky2::iop::target::BoolTarget; +use plonky2::plonk::config::AlgebraicHasher; +use plonky2::plonk::config::GenericConfig; +use plonky2::{hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; +use plonky2x::ecc::ed25519::curve::curve_types::Curve; +use plonky2x::ecc::ed25519::curve::ed25519::Ed25519; +use plonky2x::ecc::ed25519::gadgets::curve::CircuitBuilderCurve; +use plonky2x::ecc::ed25519::gadgets::eddsa::{ + verify_signatures_circuit, EDDSAPublicKeyTarget, EDDSASignatureTarget, +}; +use plonky2x::num::nonnative::nonnative::CircuitBuilderNonNative; + +use crate::utils::{ + EncTendermintHashTarget, TendermintHashTarget, ValidatorMessageTarget, HASH_SIZE_BITS, + VALIDATOR_MESSAGE_BYTES_LENGTH_MAX, +}; + +pub trait TendermintSignature, const D: usize> { + type Curve: Curve; + + // Extract a hash from a protobuf-encoded hash. + fn extract_hash_from_protobuf( + &mut self, + hash: &EncTendermintHashTarget, + ) -> TendermintHashTarget; + + /// Extract the header hash from the signed message from a validator. + fn verify_hash_in_message( + &mut self, + message: &ValidatorMessageTarget, + header_hash: &TendermintHashTarget, + // Should be the same for all validators + round_present_in_message: &BoolTarget, + ) -> TendermintHashTarget; + + /// Verifies the signatures of the validators in the validator set. + fn verify_signatures< + E: CubicParameters, + C: GenericConfig + 'static, + >( + &mut self, + // This message should be range-checked before being passed in. + messages: Vec>, + eddsa_sig_targets: Vec<&EDDSASignatureTarget>, + eddsa_pubkey_targets: Vec<&EDDSAPublicKeyTarget>, + ) where + >::Hasher: AlgebraicHasher; + + /// Verifies a single signature of a Tendermint validator. + fn verify_signature< + E: CubicParameters, + C: GenericConfig + 'static, + >( + &mut self, + // This message should be range-checked before being passed in. + message: Vec, + eddsa_sig_target: &EDDSASignatureTarget, + eddsa_pubkey_target: &EDDSAPublicKeyTarget, + ) where + >::Hasher: AlgebraicHasher; +} + +impl, const D: usize> TendermintSignature + for CircuitBuilder +{ + type Curve = Ed25519; + + fn extract_hash_from_protobuf( + &mut self, + hash: &EncTendermintHashTarget, + ) -> TendermintHashTarget { + let mut result = [self._false(); HASH_SIZE_BITS]; + // Skip first 2 bytes + for i in 0..HASH_SIZE_BITS { + result[i] = hash.0[i + (8 * 2)]; + } + TendermintHashTarget(result) + } + + fn verify_hash_in_message( + &mut self, + message: &ValidatorMessageTarget, + header_hash: &TendermintHashTarget, + // Should be the same for all validators + round_present_in_message: &BoolTarget, + ) -> TendermintHashTarget { + // Logic: + // Verify that header_hash is equal to the hash in the message at the correct index. + // If the round is missing, then the hash starts at index 16. + // If the round is present, then the hash starts at index 25. + + let missing_round_start_idx = 16; + + let including_round_start_idx = 25; + + let one = self.one(); + + let mut vec_round_missing = [self._false(); HASH_SIZE_BITS]; + + let mut vec_round_present = [self._false(); HASH_SIZE_BITS]; + + for i in 0..HASH_SIZE_BITS { + vec_round_missing[i] = message.0[(missing_round_start_idx) * 8 + i]; + vec_round_present[i] = message.0[(including_round_start_idx) * 8 + i]; + let round_missing_eq = + self.is_equal(header_hash.0[i].target, vec_round_missing[i].target); + let round_present_eq = + self.is_equal(header_hash.0[i].target, vec_round_present[i].target); + + // Pick the correct bit based on whether the round is present or not. + let hash_eq = self.select( + *round_present_in_message, + round_present_eq.target, + round_missing_eq.target, + ); + + self.connect(hash_eq, one); + } + + *header_hash + } + + /// Verifies the signatures of the validators in the validator set. + fn verify_signatures< + E: CubicParameters, + C: GenericConfig + 'static, + >( + &mut self, + // This message should be range-checked before being passed in. + messages: Vec>, + eddsa_sig_targets: Vec<&EDDSASignatureTarget>, + eddsa_pubkey_targets: Vec<&EDDSAPublicKeyTarget>, + ) where + >::Hasher: AlgebraicHasher, + { + // TODO: UPDATE message.len() to VALIDATOR_SET_SIZE_MAX + assert!( + messages.len() == eddsa_sig_targets.len() + && messages.len() == eddsa_pubkey_targets.len(), + ); + + // Already in bits + let byte_len = (messages[0].len() / 8) as u128; + + let eddsa_target = + verify_signatures_circuit::(self, messages.len(), byte_len); + + for i in 0..messages.len() { + let message = &messages[i]; + let eddsa_sig_target = eddsa_sig_targets[i]; + let eddsa_pubkey_target = eddsa_pubkey_targets[i]; + for j in 0..8 * VALIDATOR_MESSAGE_BYTES_LENGTH_MAX { + self.connect(eddsa_target.msgs[i][j].target, message[j].target); + } + + self.connect_nonnative(&eddsa_target.sigs[i].s, &eddsa_sig_target.s); + self.connect_nonnative(&eddsa_target.sigs[i].r.x, &eddsa_sig_target.r.x); + self.connect_nonnative(&eddsa_target.sigs[i].r.y, &eddsa_sig_target.r.y); + + self.connect_affine_point(&eddsa_target.pub_keys[i].0, &eddsa_pubkey_target.0); + } + } + + /// Verifies the signatures of the validators in the validator set. + fn verify_signature< + E: CubicParameters, + C: GenericConfig + 'static, + >( + &mut self, + // This should be the messaged signed by the validator that the header hash is extracted from. + // We should range check this outside of the circuit. + message: Vec, + eddsa_sig_target: &EDDSASignatureTarget, + eddsa_pubkey_target: &EDDSAPublicKeyTarget, + ) where + >::Hasher: AlgebraicHasher, + { + let message_bytes_len: usize = message.len() / 8; + let eddsa_target = verify_signatures_circuit::( + self, + 1, + message_bytes_len as u128, + ); + + for i in 0..message.len() { + self.connect(eddsa_target.msgs[0][i].target, message[i].target); + } + + self.connect_nonnative(&eddsa_target.sigs[0].s, &eddsa_sig_target.s); + self.connect_nonnative(&eddsa_target.sigs[0].r.x, &eddsa_sig_target.r.x); + self.connect_nonnative(&eddsa_target.sigs[0].r.y, &eddsa_sig_target.r.y); + + self.connect_affine_point(&eddsa_target.pub_keys[0].0, &eddsa_pubkey_target.0); + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use curta::math::goldilocks::cubic::GoldilocksCubicParameters; + use num::BigUint; + use plonky2::field::goldilocks_field::GoldilocksField; + use plonky2::field::types::Field; + use plonky2::{ + iop::witness::{PartialWitness, WitnessWrite}, + plonk::{ + circuit_builder::CircuitBuilder, circuit_data::CircuitConfig, + config::PoseidonGoldilocksConfig, + }, + }; + use plonky2x::ecc::ed25519::curve::eddsa::{verify_message, EDDSAPublicKey, EDDSASignature}; + use plonky2x::ecc::ed25519::gadgets::curve::WitnessAffinePoint; + use subtle_encoding::hex; + + use plonky2x::num::biguint::CircuitBuilderBiguint; + + use plonky2x::ecc::ed25519::curve::curve_types::AffinePoint; + use plonky2x::ecc::ed25519::field::ed25519_scalar::Ed25519Scalar; + + use crate::utils::to_be_bits; + + #[test] + fn test_verify_eddsa_signature() { + // First signature from block 11000 + let msg = "6c080211f82a00000000000022480a2036f2d954fe1ba37c5036cb3c6b366d0daf68fccbaa370d9490361c51a0a38b61122408011220cddf370e891591c9d912af175c966cd8dfa44b2c517e965416b769eb4b9d5d8d2a0c08f6b097a50610dffbcba90332076d6f6368612d33"; + let pubkey = "de25aec935b10f657b43fa97e5a8d4e523bdb0f9972605f0b064eff7b17048ba"; + let sig = "091576e9e3ad0e5ba661f7398e1adb3976ba647b579b8e4a224d1d02b591ade6aedb94d3bf55d258f089d6413155a57adfd4932418a798c2d68b29850f6fb50b"; + + let msg_bytes = hex::decode(msg).unwrap(); + let pub_key_bytes = hex::decode(pubkey).unwrap(); + let sig_bytes = hex::decode(sig).unwrap(); + + type F = GoldilocksField; + type Curve = Ed25519; + type E = GoldilocksCubicParameters; + type C = PoseidonGoldilocksConfig; + const D: usize = 2; + + let mut pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(CircuitConfig::standard_ecc_config()); + + let msg_bits = to_be_bits(msg_bytes.to_vec()); + let mut msg_bits_target = Vec::new(); + for i in 0..msg_bits.len() { + msg_bits_target.push(builder.constant_bool(msg_bits[i])); + } + + let virtual_affine_point_target = builder.add_virtual_affine_point_target(); + + let pub_key_uncompressed: AffinePoint = + AffinePoint::new_from_compressed_point(&pub_key_bytes); + + let eddsa_pub_key_target = EDDSAPublicKeyTarget(virtual_affine_point_target); + + pw.set_affine_point_target::(&eddsa_pub_key_target.0, &pub_key_uncompressed); + + let sig_r = AffinePoint::new_from_compressed_point(&sig_bytes[0..32]); + assert!(sig_r.is_valid()); + + let sig_s_biguint = BigUint::from_bytes_le(&sig_bytes[32..64]); + let sig_s = Ed25519Scalar::from_noncanonical_biguint(sig_s_biguint.clone()); + let sig = EDDSASignature { r: sig_r, s: sig_s }; + + assert!(verify_message( + &msg_bits, + &sig, + &EDDSAPublicKey(pub_key_uncompressed) + )); + println!("verified signature"); + + let sig_r_target = builder.constant_affine_point(sig_r); + let sig_s_biguint_target = builder.constant_biguint(&sig_s_biguint); + let sig_s_target = builder.biguint_to_nonnative(&sig_s_biguint_target); + + let eddsa_sig_target = EDDSASignatureTarget { + r: sig_r_target, + s: sig_s_target, + }; + + builder.verify_signature::(msg_bits_target, &eddsa_sig_target, &eddsa_pub_key_target); + + let inner_data = builder.build::(); + let inner_proof = inner_data.prove(pw).unwrap(); + inner_data.verify(inner_proof.clone()).unwrap(); + + let mut outer_builder = CircuitBuilder::::new(CircuitConfig::standard_ecc_config()); + let inner_proof_target = outer_builder.add_virtual_proof_with_pis(&inner_data.common); + let inner_verifier_data = + outer_builder.add_virtual_verifier_data(inner_data.common.config.fri_config.cap_height); + outer_builder.verify_proof::( + &inner_proof_target, + &inner_verifier_data, + &inner_data.common, + ); + + let outer_data = outer_builder.build::(); + for gate in outer_data.common.gates.iter() { + println!("ecddsa verify recursive gate: {:?}", gate); + } + + let mut outer_pw = PartialWitness::new(); + outer_pw.set_proof_with_pis_target(&inner_proof_target, &inner_proof); + outer_pw.set_verifier_data_target(&inner_verifier_data, &inner_data.verifier_only); + + let outer_proof = outer_data.prove(outer_pw).unwrap(); + + outer_data + .verify(outer_proof) + .expect("failed to verify proof"); + } +} diff --git a/src/step.rs b/src/step.rs new file mode 100644 index 00000000..856ec5e9 --- /dev/null +++ b/src/step.rs @@ -0,0 +1,591 @@ +//! The protobuf encoding of a Tendermint validator is a deterministic function of the validator's +//! public key (32 bytes) and voting power (int64). The encoding is as follows in bytes: +// +//! 10 34 10 32 16 +// +//! The `pubkey` is encoded as the raw list of bytes used in the public key. The `varint` is +//! encoded using protobuf's default integer encoding, which consist of 7 bit payloads. You can +//! read more about them here: https://protobuf.dev/programming-guides/encoding/#varints. +use curta::plonky2::field::CubicParameters; +use plonky2::field::extension::Extendable; +use plonky2::iop::target::BoolTarget; +use plonky2::plonk::config::AlgebraicHasher; +use plonky2::plonk::config::GenericConfig; +use plonky2::{hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; +use plonky2x::ecc::ed25519::curve::curve_types::Curve; +use plonky2x::ecc::ed25519::curve::ed25519::Ed25519; +use plonky2x::ecc::ed25519::gadgets::curve::CircuitBuilderCurve; +use plonky2x::ecc::ed25519::gadgets::eddsa::{EDDSAPublicKeyTarget, EDDSASignatureTarget}; +use plonky2x::num::nonnative::nonnative::CircuitBuilderNonNative; +use plonky2x::num::u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target}; + +use crate::signature::TendermintSignature; +use crate::utils::{ + EncTendermintHashTarget, I64Target, MarshalledValidatorTarget, TendermintHashTarget, + ValidatorMessageTarget, HASH_SIZE_BITS, HEADER_PROOF_DEPTH, PROTOBUF_HASH_SIZE_BITS, + VALIDATOR_MESSAGE_BYTES_LENGTH_MAX, VALIDATOR_SET_SIZE_MAX, +}; +use crate::validator::TendermintMarshaller; +use crate::voting::TendermintVoting; + +#[derive(Debug, Clone)] +pub struct ValidatorTarget { + pubkey: EDDSAPublicKeyTarget, + signature: EDDSASignatureTarget, + message: ValidatorMessageTarget, + message_byte_length: U32Target, + voting_power: I64Target, + validator_byte_length: U32Target, + enabled: BoolTarget, + signed: BoolTarget, +} + +/// The protobuf-encoded leaf (a hash), and it's corresponding proof and path indices against the header. +#[derive(Debug, Clone)] +pub struct InclusionProofTarget { + enc_leaf: EncTendermintHashTarget, + // Path and proof should have a fixed length of HEADER_PROOF_DEPTH. + path: Vec, + proof: Vec, +} + +#[derive(Debug, Clone)] +pub struct CelestiaBlockProofTarget { + validators: Vec>, + header: TendermintHashTarget, + data_hash_proof: InclusionProofTarget, + validator_hash_proof: InclusionProofTarget, + next_validators_hash_proof: InclusionProofTarget, + round_present: BoolTarget, +} + +pub trait TendermintStep, const D: usize> { + type Curve: Curve; + + /// Verifies a Tendermint consensus block. + fn step, C: GenericConfig + 'static>( + &mut self, + validators: &Vec>, + header: &TendermintHashTarget, + data_hash_proof: &InclusionProofTarget, + validator_hash_proof: &InclusionProofTarget, + next_validators_hash_proof: &InclusionProofTarget, + round_present: &BoolTarget, + ) where + >::Hasher: AlgebraicHasher; +} + +impl, const D: usize> TendermintStep for CircuitBuilder { + type Curve = Ed25519; + + fn step, C: GenericConfig + 'static>( + &mut self, + validators: &Vec>, + header: &TendermintHashTarget, + data_hash_proof: &InclusionProofTarget, + validator_hash_proof: &InclusionProofTarget, + next_validators_hash_proof: &InclusionProofTarget, + round_present: &BoolTarget, + ) where + >::Hasher: AlgebraicHasher, + { + let one = self.one(); + // Verify each of the validators marshal correctly + // Assumes the validators are sorted in the correct order + let byte_lengths: Vec = + validators.iter().map(|v| v.validator_byte_length).collect(); + let marshalled_validators: Vec = validators + .iter() + .map(|v| self.marshal_tendermint_validator(&v.pubkey.0, &v.voting_power)) + .collect(); + let validators_enabled: Vec = validators.iter().map(|v| v.enabled).collect(); + let validators_enabled_u32: Vec = validators_enabled + .iter() + .map(|v| { + let zero = self.zero_u32(); + let one = self.one_u32(); + U32Target(self.select(*v, one.0, zero.0)) + }) + .collect(); + + let validator_voting_power: Vec = + validators.iter().map(|v| v.voting_power).collect(); + + let messages: Vec> = + validators.iter().map(|v| v.message.0.to_vec()).collect(); + let signatures: Vec<&EDDSASignatureTarget> = + validators.iter().map(|v| &v.signature).collect(); + let pubkeys: Vec<&EDDSAPublicKeyTarget> = + validators.iter().map(|v| &v.pubkey).collect(); + + // // Compute the validators hash + let validators_hash_target = + self.hash_validator_set(&marshalled_validators, &byte_lengths, &validators_enabled); + + // // Assert that computed validator hash matches expected validator hash + let extracted_hash = self.extract_hash_from_protobuf(&validator_hash_proof.enc_leaf); + for i in 0..HASH_SIZE_BITS { + self.connect( + validators_hash_target.0[i].target, + extracted_hash.0[i].target, + ); + } + + let total_voting_power = self.get_total_voting_power(&validator_voting_power); + let threshold_numerator = self.constant_u32(2); + let threshold_denominator = self.constant_u32(3); + + // Assert the accumulated voting power is greater than the threshold + let check_voting_power_bool = self.check_voting_power( + &validator_voting_power, + &validators_enabled_u32, + &total_voting_power, + &threshold_numerator, + &threshold_denominator, + ); + self.connect(check_voting_power_bool.target, one); + + // // TODO: Handle dummies + self.verify_signatures::(messages, signatures, pubkeys); + + // TODO: Verify that this will work with dummy signatures + for i in 0..VALIDATOR_SET_SIZE_MAX { + // Verify that the header is in the message in the correct location + self.verify_hash_in_message(&validators[i].message, header, round_present); + } + + let header_from_data_root_proof = self.get_root_from_merkle_proof( + &data_hash_proof.proof, + &data_hash_proof.path, + &data_hash_proof.enc_leaf, + ); + let header_from_validator_root_proof = self.get_root_from_merkle_proof( + &validator_hash_proof.proof, + &validator_hash_proof.path, + &validator_hash_proof.enc_leaf, + ); + let header_from_next_validators_root_proof = self.get_root_from_merkle_proof( + &next_validators_hash_proof.proof, + &next_validators_hash_proof.path, + &next_validators_hash_proof.enc_leaf, + ); + + // Confirm that the header from the proof of {validator_hash, next_validators_hash, data_hash} all match the header + for i in 0..HASH_SIZE_BITS { + self.connect(header.0[i].target, header_from_data_root_proof.0[i].target); + self.connect( + header.0[i].target, + header_from_validator_root_proof.0[i].target, + ); + self.connect( + header.0[i].target, + header_from_next_validators_root_proof.0[i].target, + ); + } + } +} + +fn create_virtual_bool_target_array, const D: usize>( + builder: &mut CircuitBuilder, + size: usize, +) -> Vec { + let mut result = Vec::new(); + for _i in 0..size { + result.push(builder.add_virtual_bool_target_safe()); + } + result +} + +fn create_virtual_inclusion_proof_target, const D: usize>( + builder: &mut CircuitBuilder, +) -> InclusionProofTarget { + let mut proof = Vec::new(); + for _i in 0..HEADER_PROOF_DEPTH { + proof.push(TendermintHashTarget( + create_virtual_bool_target_array(builder, HASH_SIZE_BITS) + .try_into() + .unwrap(), + )); + } + InclusionProofTarget { + enc_leaf: EncTendermintHashTarget( + create_virtual_bool_target_array(builder, PROTOBUF_HASH_SIZE_BITS) + .try_into() + .unwrap(), + ), + path: create_virtual_bool_target_array(builder, HEADER_PROOF_DEPTH), + proof, + } +} + +pub fn make_step_circuit< + F: RichField + Extendable, + const D: usize, + C: Curve, + Config: GenericConfig + 'static, + E: CubicParameters, +>( + builder: &mut CircuitBuilder, +) -> CelestiaBlockProofTarget +where + Config::Hasher: AlgebraicHasher, +{ + type Curve = Ed25519; + let mut validators = Vec::new(); + for _i in 0..VALIDATOR_SET_SIZE_MAX { + let pubkey = EDDSAPublicKeyTarget(builder.add_virtual_affine_point_target()); + let signature = EDDSASignatureTarget { + r: builder.add_virtual_affine_point_target(), + s: builder.add_virtual_nonnative_target(), + }; + let message = + create_virtual_bool_target_array(builder, VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8); + let message = ValidatorMessageTarget(message.try_into().unwrap()); + + let message_byte_length = builder.add_virtual_u32_target(); + + let voting_power = I64Target([ + builder.add_virtual_u32_target(), + builder.add_virtual_u32_target(), + ]); + let validator_byte_length = builder.add_virtual_u32_target(); + let enabled = builder.add_virtual_bool_target_safe(); + let signed = builder.add_virtual_bool_target_safe(); + + validators.push(ValidatorTarget:: { + pubkey, + signature, + message, + message_byte_length, + voting_power, + validator_byte_length, + enabled, + signed, + }) + } + + let header = create_virtual_bool_target_array(builder, HASH_SIZE_BITS); + let header = TendermintHashTarget(header.try_into().unwrap()); + + let data_hash_proof = create_virtual_inclusion_proof_target(builder); + let validator_hash_proof = create_virtual_inclusion_proof_target(builder); + let next_validators_hash_proof = create_virtual_inclusion_proof_target(builder); + + let round_present = builder.add_virtual_bool_target_safe(); + + builder.step::( + &validators, + &header, + &data_hash_proof, + &validator_hash_proof, + &next_validators_hash_proof, + &round_present, + ); + + CelestiaBlockProofTarget:: { + validators, + header, + data_hash_proof, + validator_hash_proof, + next_validators_hash_proof, + round_present, + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use curta::math::goldilocks::cubic::GoldilocksCubicParameters; + use num::BigUint; + use plonky2::field::goldilocks_field::GoldilocksField; + use plonky2::field::types::Field; + use plonky2::{ + iop::witness::{PartialWitness, WitnessWrite}, + plonk::{ + circuit_builder::CircuitBuilder, circuit_data::CircuitConfig, + config::PoseidonGoldilocksConfig, + }, + }; + use plonky2x::ecc::ed25519::gadgets::curve::WitnessAffinePoint; + use plonky2x::num::biguint::WitnessBigUint; + use plonky2x::num::u32::witness::WitnessU32; + + use plonky2x::ecc::ed25519::curve::curve_types::AffinePoint; + use plonky2x::ecc::ed25519::field::ed25519_scalar::Ed25519Scalar; + + use crate::inputs::{generate_step_inputs, CelestiaBlockProof}; + use crate::utils::{to_be_bits, VALIDATOR_SET_SIZE_MAX}; + + use log; + use plonky2::timed; + use plonky2::util::timing::TimingTree; + use subtle_encoding::hex; + + type C = PoseidonGoldilocksConfig; + type F = >::F; + const D: usize = 2; + + #[test] + fn test_verify_hash_in_message() { + // This is a test case generated from block 144094 of Celestia's Mocha testnet + // Block Hash: 8909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c (needs to be lower case) + // Signed Message (from the last validator): 6b080211de3202000000000022480a208909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c12240801122061263df4855e55fcab7aab0a53ee32cf4f29a1101b56de4a9d249d44e4cf96282a0b089dce84a60610ebb7a81932076d6f6368612d33 + // No round exists in present the message that was signed above + + let header_hash = "8909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c"; + let header_bits = to_be_bits(hex::decode(header_hash).unwrap()); + + let signed_message = "6b080211de3202000000000022480a208909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c12240801122061263df4855e55fcab7aab0a53ee32cf4f29a1101b56de4a9d249d44e4cf96282a0b089dce84a60610ebb7a81932076d6f6368612d33"; + let signed_message_bits = to_be_bits(hex::decode(signed_message).unwrap()); + + let mut pw = PartialWitness::new(); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let zero = builder._false(); + + let mut signed_message_target = [builder._false(); VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8]; + for i in 0..signed_message_bits.len() { + signed_message_target[i] = builder.constant_bool(signed_message_bits[i]); + } + + let mut header_hash_target = [builder._false(); HASH_SIZE_BITS]; + for i in 0..header_bits.len() { + header_hash_target[i] = builder.constant_bool(header_bits[i]); + } + + let result = builder.verify_hash_in_message( + &ValidatorMessageTarget(signed_message_target), + &TendermintHashTarget(header_hash_target), + &zero, + ); + + for i in 0..HASH_SIZE_BITS { + if header_bits[i] { + pw.set_target(result.0[i].target, F::ONE); + } else { + pw.set_target(result.0[i].target, F::ZERO); + } + } + + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + println!("Created proof"); + + data.verify(proof).unwrap(); + + println!("Verified proof"); + } + + #[test] + fn test_step() { + let _ = env_logger::builder().is_test(true).try_init(); + let mut timing = TimingTree::new("Celestia Header Verify", log::Level::Debug); + + let mut pw = PartialWitness::new(); + let config = CircuitConfig::standard_ecc_config(); + let mut builder = CircuitBuilder::::new(config); + + type F = GoldilocksField; + type Curve = Ed25519; + type E = GoldilocksCubicParameters; + type C = PoseidonGoldilocksConfig; + const D: usize = 2; + + let celestia_proof_target = + make_step_circuit::(&mut builder); + + // Testing block 11000 + let block = 11000; + let celestia_block_proof: CelestiaBlockProof = generate_step_inputs(block); + timed!(timing, "assigning inputs", { + // Set target for header + let header_bits = to_be_bits(celestia_block_proof.header); + for i in 0..HASH_SIZE_BITS { + pw.set_bool_target(celestia_proof_target.header.0[i], header_bits[i]); + } + + // Set target for round present + pw.set_bool_target( + celestia_proof_target.round_present, + celestia_block_proof.round_present, + ); + + // Set the encoded leaf for each of the proofs + let data_hash_enc_leaf = to_be_bits(celestia_block_proof.data_hash_proof.enc_leaf); + let val_hash_enc_leaf = to_be_bits(celestia_block_proof.validator_hash_proof.enc_leaf); + let next_val_hash_enc_leaf = + to_be_bits(celestia_block_proof.next_validators_hash_proof.enc_leaf); + + for i in 0..PROTOBUF_HASH_SIZE_BITS { + pw.set_bool_target( + celestia_proof_target.data_hash_proof.enc_leaf.0[i], + data_hash_enc_leaf[i], + ); + pw.set_bool_target( + celestia_proof_target.validator_hash_proof.enc_leaf.0[i], + val_hash_enc_leaf[i], + ); + pw.set_bool_target( + celestia_proof_target.next_validators_hash_proof.enc_leaf.0[i], + next_val_hash_enc_leaf[i], + ); + } + + for i in 0..HEADER_PROOF_DEPTH { + // Set path indices for each of the proof indices + pw.set_bool_target( + celestia_proof_target.data_hash_proof.path[i], + celestia_block_proof.data_hash_proof.path[i], + ); + pw.set_bool_target( + celestia_proof_target.validator_hash_proof.path[i], + celestia_block_proof.validator_hash_proof.path[i], + ); + pw.set_bool_target( + celestia_proof_target.next_validators_hash_proof.path[i], + celestia_block_proof.next_validators_hash_proof.path[i], + ); + + let data_hash_aunt = + to_be_bits(celestia_block_proof.data_hash_proof.proof[i].to_vec()); + + let val_hash_aunt = + to_be_bits(celestia_block_proof.validator_hash_proof.proof[i].to_vec()); + + let next_val_aunt = + to_be_bits(celestia_block_proof.next_validators_hash_proof.proof[i].to_vec()); + + // Set aunts for each of the proofs + for j in 0..HASH_SIZE_BITS { + pw.set_bool_target( + celestia_proof_target.data_hash_proof.proof[i].0[j], + data_hash_aunt[j], + ); + pw.set_bool_target( + celestia_proof_target.validator_hash_proof.proof[i].0[j], + val_hash_aunt[j], + ); + pw.set_bool_target( + celestia_proof_target.next_validators_hash_proof.proof[i].0[j], + next_val_aunt[j], + ); + } + } + + // Set the targets for each of the validators + for i in 0..VALIDATOR_SET_SIZE_MAX { + let validator = &celestia_block_proof.validators[i]; + let signature_bytes = validator.signature.clone().into_bytes(); + + let voting_power_lower = (validator.voting_power & ((1 << 32) - 1)) as u32; + let voting_power_upper = (validator.voting_power >> 32) as u32; + + let pub_key_uncompressed: AffinePoint = + AffinePoint::new_from_compressed_point(validator.pubkey.as_bytes()); + + let sig_r: AffinePoint = + AffinePoint::new_from_compressed_point(&signature_bytes[0..32]); + assert!(sig_r.is_valid()); + + let sig_s_biguint = BigUint::from_bytes_le(&signature_bytes[32..64]); + let _sig_s = Ed25519Scalar::from_noncanonical_biguint(sig_s_biguint.clone()); + + // Set the targets for the public key + pw.set_affine_point_target( + &celestia_proof_target.validators[i].pubkey.0, + &pub_key_uncompressed, + ); + + // Set signature targets + pw.set_affine_point_target( + &celestia_proof_target.validators[i].signature.r, + &sig_r, + ); + pw.set_biguint_target( + &celestia_proof_target.validators[i].signature.s.value, + &sig_s_biguint, + ); + + let message_bits = to_be_bits(validator.message.clone()); + // Set messages for each of the proofs + for j in 0..VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8 { + pw.set_bool_target( + celestia_proof_target.validators[i].message.0[j], + message_bits[j], + ); + } + + // Set voting power targets + pw.set_u32_target( + celestia_proof_target.validators[i].voting_power.0[0], + voting_power_lower, + ); + pw.set_u32_target( + celestia_proof_target.validators[i].voting_power.0[1], + voting_power_upper, + ); + + // Set length targets + pw.set_u32_target( + celestia_proof_target.validators[i].validator_byte_length, + validator.validator_byte_length as u32, + ); + pw.set_u32_target( + celestia_proof_target.validators[i].message_byte_length, + validator.message_byte_length as u32, + ); + + // Set enabled and signed + pw.set_bool_target( + celestia_proof_target.validators[i].enabled, + validator.enabled, + ); + pw.set_bool_target(celestia_proof_target.validators[i].signed, validator.signed); + } + }); + let inner_data = builder.build::(); + timed!(timing, "Generate proof", { + let inner_proof = timed!( + timing, + "Total proof with a recursive envelope", + plonky2::plonk::prover::prove( + &inner_data.prover_only, + &inner_data.common, + pw, + &mut timing + ) + .unwrap() + ); + inner_data.verify(inner_proof.clone()).unwrap(); + + // let mut outer_builder = CircuitBuilder::::new(CircuitConfig::standard_ecc_config()); + // let inner_proof_target = outer_builder.add_virtual_proof_with_pis(&inner_data.common); + // let inner_verifier_data = + // outer_builder.add_virtual_verifier_data(inner_data.common.config.fri_config.cap_height); + // outer_builder.verify_proof::( + // &inner_proof_target, + // &inner_verifier_data, + // &inner_data.common, + // ); + + // let outer_data = outer_builder.build::(); + // for gate in outer_data.common.gates.iter() { + // println!("ecddsa verify recursive gate: {:?}", gate); + // } + + // let mut outer_pw = PartialWitness::new(); + // outer_pw.set_proof_with_pis_target(&inner_proof_target, &inner_proof); + // outer_pw.set_verifier_data_target(&inner_verifier_data, &inner_data.verifier_only); + + // let outer_proof = outer_data.prove(outer_pw).unwrap(); + + // outer_data + // .verify(outer_proof) + // .expect("failed to verify proof"); + }); + + timing.print(); + } +} diff --git a/src/utils.rs b/src/utils.rs index c8002e82..38906ebd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,98 @@ use plonky2::hash::hash_types::RichField; +use plonky2::iop::target::BoolTarget; +use plonky2x::ecc::ed25519::curve::curve_types::Curve; +use plonky2x::ecc::ed25519::gadgets::eddsa::EDDSAPublicKeyTarget; +use plonky2x::num::u32::gadgets::arithmetic_u32::U32Target; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::rc::Rc; +use subtle_encoding::hex; +use tendermint::merkle::HASH_SIZE; +/// Source (tendermint-rs): https://github.com/informalsystems/tendermint-rs/blob/e930691a5639ef805c399743ac0ddbba0e9f53da/tendermint/src/merkle.rs#L32 +use tendermint::{ + block::Header, + block::{Commit, CommitSig}, + merkle::{Hash, MerkleHash}, + validator::{Info, Set as ValidatorSet}, + vote::Power, + vote::{ValidatorIndex, Vote}, +}; +use tendermint_proto::{ + types::BlockId as RawBlockId, types::Data as RawData, + version::Consensus as RawConsensusVersion, Protobuf, +}; + +/// The number of bytes in a SHA256 hash. +pub const HASH_SIZE_BITS: usize = HASH_SIZE * 8; + +/// The number of bytes in a protobuf-encoded SHA256 hash. +pub const PROTOBUF_HASH_SIZE_BITS: usize = HASH_SIZE_BITS + 8 * 2; + +// Depth of the proofs against the header. +pub const HEADER_PROOF_DEPTH: usize = 4; + +/// The maximum length of a protobuf-encoded Tendermint validator in bytes. +pub const VALIDATOR_BYTE_LENGTH_MAX: usize = 46; + +/// The maximum length of a protobuf-encoded Tendermint validator in bits. +pub const VALIDATOR_BIT_LENGTH_MAX: usize = VALIDATOR_BYTE_LENGTH_MAX * 8; + +/// The minimum length of a protobuf-encoded Tendermint validator in bytes. +pub const VALIDATOR_BYTE_LENGTH_MIN: usize = 38; + +/// The minimum length of a protobuf-encoded Tendermint validator in bits. +const _VALIDATOR_BIT_LENGTH_MIN: usize = VALIDATOR_BYTE_LENGTH_MIN * 8; + +/// The number of possible byte lengths of a protobuf-encoded Tendermint validator. +pub const NUM_POSSIBLE_VALIDATOR_BYTE_LENGTHS: usize = + VALIDATOR_BYTE_LENGTH_MAX - VALIDATOR_BYTE_LENGTH_MIN + 1; + +// The number of bytes in a Tendermint validator's public key. +const _PUBKEY_BYTES_LEN: usize = 32; + +// The maximum number of bytes in a Tendermint validator's voting power. +// https://docs.tendermint.com/v0.34/tendermint-core/using-tendermint.html#tendermint-networks +pub const VOTING_POWER_BYTES_LENGTH_MAX: usize = 9; + +// The maximum number of bits in a Tendermint validator's voting power. +pub const VOTING_POWER_BITS_LENGTH_MAX: usize = VOTING_POWER_BYTES_LENGTH_MAX * 8; + +// The maximum number of validators in a Tendermint validator set. +pub const VALIDATOR_SET_SIZE_MAX: usize = 4; + +// The maximum number of bytes in a validator message (CanonicalVote toSignBytes). +// const VALIDATOR_MESSAGE_BYTES_LENGTH_MAX: usize = 124; +pub const VALIDATOR_MESSAGE_BYTES_LENGTH_MAX: usize = 109; + +/// A protobuf-encoded tendermint hash as a 34 byte target. +#[derive(Debug, Clone, Copy)] +pub struct EncTendermintHashTarget(pub [BoolTarget; PROTOBUF_HASH_SIZE_BITS]); + +/// The Tendermint hash as a 32 byte target. +#[derive(Debug, Clone, Copy)] +pub struct TendermintHashTarget(pub [BoolTarget; HASH_SIZE_BITS]); + +/// The marshalled validator bits as a target. +#[derive(Debug, Clone, Copy)] +pub struct MarshalledValidatorTarget(pub [BoolTarget; VALIDATOR_BIT_LENGTH_MAX]); + +/// The voting power as a list of 2 u32 targets. +#[derive(Debug, Clone, Copy)] +pub struct I64Target(pub [U32Target; 2]); + +/// The message signed by the validator as a target. +#[derive(Debug, Clone, Copy)] +pub struct ValidatorMessageTarget(pub [BoolTarget; VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8]); + +/// The bytes, public key, and voting power targets inside of a Tendermint validator. +#[derive(Debug, Clone)] +pub struct TendermintValidator { + pub pubkey: EDDSAPublicKeyTarget, + pub voting_power: I64Target, +} + pub fn bits_to_bytes(bits: &[bool]) -> Vec { let mut bytes = Vec::new(); let nb_bytes = if bits.len() % 8 == 0 { @@ -49,3 +142,649 @@ pub fn bytes_to_le_f_bits(bytes: &[u8]) -> Vec { } bits } + +pub fn to_be_bits(msg: Vec) -> Vec { + let mut res = Vec::new(); + for i in 0..msg.len() { + let char = msg[i]; + for j in 0..8 { + if (char & (1 << 7 - j)) != 0 { + res.push(true); + } else { + res.push(false); + } + } + } + res +} + +pub fn to_le_bits(msg: Vec) -> Vec { + let mut res = Vec::new(); + for i in 0..msg.len() { + let char = msg[i]; + for j in 0..8 { + if (char & (1 << j)) != 0 { + res.push(true); + } else { + res.push(false); + } + } + } + res +} + +/* +* Mocking comet-bft proof logic in Rust +* TODO: Upstream to tendermint-rs +*/ + +/// Compute leaf hashes for arbitrary byte vectors. +/// The leaves of the tree are the bytes of the given byte vectors in +/// the given order. +pub fn hash_all_leaves(byte_vecs: &[impl AsRef<[u8]>]) -> Vec +where + H: MerkleHash + Default, +{ + let mut _hasher = H::default(); + let hashed_leaves = byte_vecs + .iter() + .map(|b| leaf_hash::(b.as_ref())) + .collect(); + hashed_leaves +} + +// Note: Implementations of ValidatorSet and SignedBlock differ in tendermint-rs and comet-bft +// Note: Following PR needs to be merged in tendermint-rs to remove TempValidatorSet and TempSignedBlock: https://github.com/informalsystems/tendermint-rs/pull/1340 +/// Validator set contains a vector of validators +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct TempValidatorSet { + pub validators: Vec, + pub proposer: Option, + pub total_voting_power: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct TempSignedBlock { + /// Block header + pub header: Header, + + /// Transaction data + pub data: RawData, + + /// Commit + pub commit: Commit, + + /// Validator set + pub validator_set: TempValidatorSet, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct SignedBlock { + /// Block header + pub header: Header, + + /// Transaction data + pub data: RawData, + + /// Commit + pub commit: Commit, + + /// Validator set + pub validator_set: ValidatorSet, +} + +// Note: Matches the implementation in tendermint-rs, need to add PR to tendermint-rs to support proofs +// https://github.com/tendermint/tendermint/blob/35581cf54ec436b8c37fabb43fdaa3f48339a170/crypto/merkle/proof.go#L35-L236 +#[derive(Clone)] +pub struct Proof { + pub total: u64, + pub index: u64, + pub leaf_hash: Hash, + pub aunts: Vec, +} + +#[derive(Clone)] +pub struct ProofNode { + pub hash: Hash, + pub left: Option>>, + pub right: Option>>, + pub parent: Option>>, +} + +impl Proof { + fn new(total: u64, index: u64, leaf_hash: Hash, aunts: Vec) -> Self { + Proof { + total, + index, + leaf_hash, + aunts, + } + } + + fn compute_root_hash(&self) -> Option { + compute_hash_from_aunts(self.index, self.total, self.leaf_hash, self.aunts.clone()) + } + + pub fn verify(&self, root_hash: &Hash, leaf: &[u8]) -> Result<(), Box> { + let leaf_hash = leaf_hash::(leaf); + println!("leaf_hash: {:?}", String::from_utf8(hex::encode(leaf_hash))); + if self.leaf_hash != leaf_hash { + return Err(format!( + "invalid leaf hash: wanted {:?} got {:?}", + hex::encode(leaf_hash), + hex::encode(self.leaf_hash) + ) + .into()); + } + let computed_hash = self + .compute_root_hash() + .expect("failed to compute root hash"); + if computed_hash != *root_hash { + return Err(format!( + "invalid root hash: wanted {:?} got {:?}", + hex::encode(root_hash), + hex::encode(computed_hash) + ) + .into()); + } + Ok(()) + } +} + +impl ProofNode { + fn new( + hash: Hash, + parent: Option>>, + left: Option>>, + right: Option>>, + ) -> Self { + ProofNode { + hash, + parent, + left, + right, + } + } + + fn flatten_aunts(&self) -> Vec { + let mut inner_hashes = Vec::new(); + let mut current_node = Some(Rc::new(RefCell::new(self.clone()))); + + while let Some(node) = current_node { + // Separate this into two steps to avoid holding onto a borrow across loop iterations + let (left, right) = { + let node_borrowed = node.borrow(); + (node_borrowed.left.clone(), node_borrowed.right.clone()) + }; + + match (&left, &right) { + (Some(left_node), _) => inner_hashes.push(left_node.borrow().hash), + (_, Some(right_node)) => inner_hashes.push(right_node.borrow().hash), + _ => {} + } + + // Now update current_node + current_node = node.borrow().parent.clone(); + } + + inner_hashes + } +} + +fn compute_hash_from_aunts( + index: u64, + total: u64, + leaf_hash: Hash, + inner_hashes: Vec, +) -> Option { + if index >= total || total == 0 { + return None; + } + match total { + 0 => panic!("Cannot call compute_hash_from_aunts() with 0 total"), + 1 => { + if !inner_hashes.is_empty() { + return None; + } + Some(leaf_hash) + } + _ => { + if inner_hashes.is_empty() { + return None; + } + let num_left = get_split_point(total as usize) as u64; + if index < num_left { + let left_hash = compute_hash_from_aunts( + index, + num_left, + leaf_hash, + inner_hashes[..inner_hashes.len() - 1].to_vec(), + ); + match left_hash { + None => return None, + Some(hash) => { + return Some(inner_hash::( + hash, + inner_hashes[inner_hashes.len() - 1], + )) + } + } + } + let right_hash = compute_hash_from_aunts( + index - num_left, + total - num_left, + leaf_hash, + inner_hashes[..inner_hashes.len() - 1].to_vec(), + ); + match right_hash { + None => None, + Some(hash) => Some(inner_hash::( + inner_hashes[inner_hashes.len() - 1], + hash, + )), + } + } + } +} + +fn proofs_from_byte_slices(items: Vec>) -> (Hash, Vec) { + let (trails, root) = trails_from_byte_slices(items.clone()); + let root_hash = root.borrow().hash; + let mut proofs = Vec::new(); + + for (i, trail) in trails.into_iter().enumerate() { + proofs.push(Proof::new( + items.len() as u64, + i as u64, + trail.borrow().hash, + trail.borrow().flatten_aunts(), + )); + } + + (root_hash, proofs) +} + +// Create trail from byte slice to root +fn trails_from_byte_slices( + items: Vec>, +) -> (Vec>>, Rc>) { + match items.len() { + 0 => { + let node = ProofNode::new(empty_hash(), None, None, None); + (vec![], Rc::new(RefCell::new(node))) + } + 1 => { + let node = Rc::new(RefCell::new(ProofNode::new( + leaf_hash::(&items[0]), + None, + None, + None, + ))); + + (vec![Rc::clone(&node)], Rc::clone(&node)) + } + _ => { + let k = get_split_point(items.len()); + let (lefts, left_root) = trails_from_byte_slices(items[..k].to_vec()); + let (rights, right_root) = trails_from_byte_slices(items[k..].to_vec()); + + let root_hash = inner_hash::(left_root.borrow().hash, right_root.borrow().hash); + let root = Rc::new(RefCell::new(ProofNode::new(root_hash, None, None, None))); + + { + let mut left_root_borrowed = (*left_root).borrow_mut(); + left_root_borrowed.parent = Some(Rc::clone(&root)); + left_root_borrowed.right = Some(Rc::clone(&right_root)); + } + { + let mut right_root_borrowed = (*right_root).borrow_mut(); + right_root_borrowed.parent = Some(Rc::clone(&root)); + right_root_borrowed.left = Some(Rc::clone(&left_root)); + } + + let trails = [lefts, rights].concat(); + + (trails, root) + } + } +} + +pub fn get_split_point(length: usize) -> usize { + if length < 1 { + panic!("Trying to split a tree with size < 1") + } + let bitlen = (length as f64).log2() as usize; + let k = 1 << bitlen; + if k == length { + k >> 1 + } else { + k + } +} + +fn empty_hash() -> Hash { + Sha256::digest([]) + .to_vec() + .try_into() + .expect("slice with incorrect length") +} + +pub fn leaf_hash(leaf: &[u8]) -> Hash +where + H: MerkleHash + Default, +{ + let mut hasher = H::default(); + hasher.leaf_hash(leaf) +} + +pub fn inner_hash(left: Hash, right: Hash) -> Hash +where + H: MerkleHash + Default, +{ + let mut hasher = H::default(); + hasher.inner_hash(left, right) +} + +pub fn generate_proofs_from_header(h: &Header) -> (Hash, Vec) { + let fields_bytes = vec![ + Protobuf::::encode_vec(h.version), + h.chain_id.clone().encode_vec(), + h.height.encode_vec(), + h.time.encode_vec(), + Protobuf::::encode_vec(h.last_block_id.unwrap_or_default()), + h.last_commit_hash.unwrap_or_default().encode_vec(), + h.data_hash.unwrap_or_default().encode_vec(), + h.validators_hash.encode_vec(), + h.next_validators_hash.encode_vec(), + h.consensus_hash.encode_vec(), + h.app_hash.clone().encode_vec(), + h.last_results_hash.unwrap_or_default().encode_vec(), + h.evidence_hash.unwrap_or_default().encode_vec(), + h.proposer_address.encode_vec(), + ]; + + proofs_from_byte_slices(fields_bytes) +} + +// Gets the vote struct: https://github.com/informalsystems/tendermint-rs/blob/c2b5c9e01eab1c740598aa14375a7453f3bfa436/light-client-verifier/src/operations/voting_power.rs#L202-L238 +pub fn non_absent_vote( + commit_sig: &CommitSig, + validator_index: ValidatorIndex, + commit: &Commit, +) -> Option { + // Cast the raw commit sig to a commit sig + let (validator_address, timestamp, signature, block_id) = match commit_sig { + CommitSig::BlockIdFlagAbsent { .. } => return None, + CommitSig::BlockIdFlagCommit { + validator_address, + timestamp, + signature, + } => ( + validator_address, + timestamp, + signature, + Some(commit.block_id), + ), + CommitSig::BlockIdFlagNil { + validator_address, + timestamp, + signature, + } => (validator_address, timestamp, signature, None), + }; + + Some(Vote { + vote_type: tendermint::vote::Type::Precommit, + height: commit.height, + round: commit.round, + block_id, + timestamp: Some(*timestamp), + validator_address: *validator_address, + validator_index, + signature: signature.clone(), + extension: Default::default(), + extension_signature: None, + }) +} + +#[cfg(test)] +pub(crate) mod tests { + use sha2::Sha256; + use subtle_encoding::hex; + use tendermint_proto::{types::SimpleValidator as RawSimpleValidator, Protobuf}; + + use super::{generate_proofs_from_header, TempSignedBlock}; + use tendermint::{ + merkle::simple_hash_from_byte_vectors, + validator::{Set as ValidatorSet, SimpleValidator}, + vote::{SignedVote, ValidatorIndex}, + }; + + use super::{inner_hash, leaf_hash, non_absent_vote, SignedBlock}; + + #[test] + fn test_validator_inclusion() { + // These are test cases generated from querying `cosmos-hub` + // for the validator set at height 0 for validator 0. + + // let root_hash = [125u8, 130, 148, 137, 132, 154, 188, 169, 153, 181, 72, 1, 150, 95, 7, 68, 137, 114, 181, 223, 226, 151, 52, 72, 170, 185, 171, 167, 154, 96, 187, 240]; + // Total: 180 + let leaf_root_hex = "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"; + let leaf_string = "L123456"; + + let leaf_root = &hex::decode(leaf_root_hex).unwrap(); + let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; + + let root = simple_hash_from_byte_vectors::(&leaf_tree); + assert_eq!(leaf_root, &root); + } + + #[test] + fn test_multiple_validator_inclusion() { + // These are test cases generated from generating a random set of validators with a byte length of 38. + + // Serde JSON + let leaf_root_hex = "5541a94a9cf19e568401a2eed59f4ac8118c945d37803632aad655c6ee4f3ed6"; + + // JSON string + let validators = vec![ + "de6ad0941095ada2a7996e6a888581928203b8b69e07ee254d289f5b9c9caea193c2ab01902d", + "92fbe0c52937d80c5ea643c7832620b84bfdf154ec7129b8b471a63a763f2fe955af1ac65fd3", + "e902f88b2371ff6243bf4b0ebe8f46205e00749dd4dad07b2ea34350a1f9ceedb7620ab913c2", + ]; + + // Process the JSON value + for validator in &validators { + println!("Validator: {}", validator); + } + + let bytes_vec: Vec> = validators.iter().map(|s| hex::decode(s).unwrap()).collect(); + + let leaf_root = &hex::decode(leaf_root_hex).unwrap(); + let leaf_tree: Vec> = bytes_vec; + + let root = simple_hash_from_byte_vectors::(&leaf_tree); + assert_eq!(leaf_root, &root); + } + + #[test] + fn test_generate_validator_hash_proof() { + // Generate test cases from Celestia block: + let temp_block = TempSignedBlock::from( + serde_json::from_str::(include_str!( + "./fixtures/signed_celestia_block.json" + )) + .unwrap(), + ); + + // Cast to SignedBlock + let block = SignedBlock { + header: temp_block.header, + data: temp_block.data, + commit: temp_block.commit, + validator_set: ValidatorSet::new( + temp_block.validator_set.validators, + temp_block.validator_set.proposer, + ), + }; + + for validator in block.validator_set.validators() { + println!("Validator: {:?}", validator); + let encoded_bytes = + Protobuf::::encode_vec(SimpleValidator::from(validator)); + println!( + "Encoded Validator (Hex): {:?}", + String::from_utf8(hex::encode(encoded_bytes)) + ); + } + + let validator_hash = block.validator_set.hash(); + + // Check that the computed hash and validators_hash match + assert_eq!(validator_hash, block.header.validators_hash); + } + + #[test] + fn test_verify_signatures() { + // Generate test cases from Celestia block: + let temp_block = Box::new(TempSignedBlock::from( + serde_json::from_str::(include_str!( + "./fixtures/signed_celestia_block.json" + )) + .unwrap(), + )); + + // Cast to SignedBlock + let block = Box::new(SignedBlock { + header: temp_block.header, + data: temp_block.data, + commit: temp_block.commit, + validator_set: ValidatorSet::new( + temp_block.validator_set.validators, + temp_block.validator_set.proposer, + ), + }); + + // Source: https://github.com/informalsystems/tendermint-rs/blob/c2b5c9e01eab1c740598aa14375a7453f3bfa436/light-client-verifier/src/operations/voting_power.rs#L139-L198 + // Verify each of the signatures of the non_absent_votes + // Verify signatures + let non_absent_votes = + block + .commit + .signatures + .iter() + .enumerate() + .filter_map(|(idx, signature)| { + ValidatorIndex::try_from(idx) + .ok() + .and_then(|validator_idx| { + non_absent_vote(signature, validator_idx, &block.commit) + .map(|vote| (signature, vote)) + }) + }); + + let mut min_sign_bytes_len = 1000000; + let mut max_sign_bytes_len = 0; + + for (_, vote) in non_absent_votes { + let validator = Box::new( + match block.validator_set.validator(vote.validator_address) { + Some(validator) => validator, + None => continue, // Cannot find matching validator, so we skip the vote + }, + ); + + // Cast the vote into a signedVote struct (which is used to get the signed bytes) + let signed_vote = Box::new( + SignedVote::from_vote(vote.clone(), block.header.chain_id.clone()) + .expect("missing signature"), + ); + + let _pub_key = validator.pub_key.ed25519().unwrap(); + + // Get the encoded signed vote bytes + // https://github.com/celestiaorg/celestia-core/blob/main/proto/tendermint/types/canonical.proto#L30-L37 + let sign_bytes = signed_vote.sign_bytes(); + + if sign_bytes.len() < min_sign_bytes_len { + min_sign_bytes_len = sign_bytes.len(); + } + if sign_bytes.len() > max_sign_bytes_len { + max_sign_bytes_len = sign_bytes.len(); + } + + // Similar to encoding the vote: https://github.com/informalsystems/tendermint-rs/blob/c2b5c9e01eab1c740598aa14375a7453f3bfa436/tendermint/src/vote.rs#L267-L271 + // let decoded_vote: CanonicalVote = Protobuf::::decode_length_delimited_vec(&sign_bytes).expect("failed to decode sign_bytes"); + + // Verify that the message signed is in fact the sign_bytes + validator + .verify_signature::( + &sign_bytes, + signed_vote.signature(), + ) + .expect("invalid signature"); + + // TODO: We can break out of the loop when we have enough voting power. + // See https://github.com/informalsystems/tendermint-rs/issues/235 + } + + let validator_hash = block.validator_set.hash(); + + // Check that the computed hash and validators_hash match + assert_eq!(validator_hash, block.header.validators_hash); + + let header_hash = block.header.hash(); + let header_hash_bytes = block.commit.block_id.hash; + assert_eq!(header_hash, header_hash_bytes); + } + + #[test] + fn test_verify_validator_hash_from_root_proof() { + // Generate test cases from Celestia block: + let block = tendermint::Block::from( + serde_json::from_str::(include_str!( + "./fixtures/celestia_block.json" + )) + .unwrap(), + ); + + let (root_hash, proofs) = generate_proofs_from_header(&block.header); + + let validator_hash_index = 7; + + // Verify validator proof + proofs[validator_hash_index] + .verify(&root_hash, &block.header.validators_hash.encode_vec()) + .unwrap(); + + // Verify proof using aunts + let mut path_indices = vec![]; + let mut path_values = vec![]; + for i in 0..proofs[validator_hash_index].aunts.len() { + path_values.push(proofs[validator_hash_index].aunts[i]); + } + + let mut current_total = proofs[validator_hash_index].total; + let mut current_index = proofs[validator_hash_index].index; + while current_total >= 1 { + path_indices.push(current_index % 2 == 1); + current_total = current_total / 2; + current_index = current_index / 2; + } + + let validators_hash = block.header.validators_hash.encode_vec(); + let leaf_hash = leaf_hash::(&validators_hash); + + let mut current_hash = leaf_hash; + for i in 0..path_indices.len() { + if path_indices[i] { + current_hash = inner_hash::(path_values[i], current_hash); + } else { + current_hash = inner_hash::(current_hash, path_values[i]); + } + } + + assert_eq!(current_hash, root_hash); + } +} diff --git a/src/validator.rs b/src/validator.rs index ef8dc7d4..fa4f825f 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -9,130 +9,23 @@ use curta::plonky2::field::CubicParameters; use plonky2::field::extension::Extendable; use plonky2::iop::target::BoolTarget; -use plonky2::plonk::config::AlgebraicHasher; -use plonky2::plonk::config::GenericConfig; use plonky2::{hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; use plonky2x::ecc::ed25519::curve::curve_types::Curve; use plonky2x::ecc::ed25519::curve::ed25519::Ed25519; use plonky2x::ecc::ed25519::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve}; -use plonky2x::ecc::ed25519::gadgets::eddsa::{ - verify_signatures_circuit, EDDSAPublicKeyTarget, EDDSASignatureTarget, -}; use plonky2x::hash::sha::sha256::sha256; -use plonky2x::num::nonnative::nonnative::CircuitBuilderNonNative; use plonky2x::num::u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target}; -use tendermint::merkle::HASH_SIZE; - -/// The number of bytes in a SHA256 hash. -pub const HASH_SIZE_BITS: usize = HASH_SIZE * 8; - -/// The number of bytes in a protobuf-encoded SHA256 hash. -pub const PROTOBUF_HASH_SIZE_BITS: usize = HASH_SIZE_BITS + 8 * 2; - -// Depth of the proofs against the header. -pub const HEADER_PROOF_DEPTH: usize = 4; - -/// The maximum length of a protobuf-encoded Tendermint validator in bytes. -const VALIDATOR_BYTE_LENGTH_MAX: usize = 46; - -/// The maximum length of a protobuf-encoded Tendermint validator in bits. -const VALIDATOR_BIT_LENGTH_MAX: usize = VALIDATOR_BYTE_LENGTH_MAX * 8; - -/// The minimum length of a protobuf-encoded Tendermint validator in bytes. -const VALIDATOR_BYTE_LENGTH_MIN: usize = 38; - -/// The minimum length of a protobuf-encoded Tendermint validator in bits. -const _VALIDATOR_BIT_LENGTH_MIN: usize = VALIDATOR_BYTE_LENGTH_MIN * 8; - -/// The number of possible byte lengths of a protobuf-encoded Tendermint validator. -const NUM_POSSIBLE_VALIDATOR_BYTE_LENGTHS: usize = - VALIDATOR_BYTE_LENGTH_MAX - VALIDATOR_BYTE_LENGTH_MIN + 1; - -// The number of bytes in a Tendermint validator's public key. -const _PUBKEY_BYTES_LEN: usize = 32; - -// The maximum number of bytes in a Tendermint validator's voting power. -// https://docs.tendermint.com/v0.34/tendermint-core/using-tendermint.html#tendermint-networks -const VOTING_POWER_BYTES_LENGTH_MAX: usize = 9; - -// The maximum number of bits in a Tendermint validator's voting power. -const VOTING_POWER_BITS_LENGTH_MAX: usize = VOTING_POWER_BYTES_LENGTH_MAX * 8; - -// The maximum number of validators in a Tendermint validator set. -pub const VALIDATOR_SET_SIZE_MAX: usize = 4; - -// The maximum number of bytes in a validator message (CanonicalVote toSignBytes). -// const VALIDATOR_MESSAGE_BYTES_LENGTH_MAX: usize = 124; -const VALIDATOR_MESSAGE_BYTES_LENGTH_MAX: usize = 109; - -/// A protobuf-encoded tendermint hash as a 34 byte target. -#[derive(Debug, Clone, Copy)] -pub struct EncTendermintHashTarget(pub [BoolTarget; PROTOBUF_HASH_SIZE_BITS]); - -/// The Tendermint hash as a 32 byte target. -#[derive(Debug, Clone, Copy)] -pub struct TendermintHashTarget(pub [BoolTarget; HASH_SIZE_BITS]); - -/// The marshalled validator bits as a target. -#[derive(Debug, Clone, Copy)] -pub struct MarshalledValidatorTarget(pub [BoolTarget; VALIDATOR_BIT_LENGTH_MAX]); - -/// The voting power as a list of 2 u32 targets. -#[derive(Debug, Clone, Copy)] -pub struct I64Target(pub [U32Target; 2]); - -/// The message signed by the validator as a target. -#[derive(Debug, Clone, Copy)] -pub struct ValidatorMessageTarget(pub [BoolTarget; VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8]); - -/// The bytes, public key, and voting power targets inside of a Tendermint validator. -#[derive(Debug, Clone)] -pub struct TendermintValidator { - pub pubkey: EDDSAPublicKeyTarget, - pub voting_power: I64Target, -} - -#[derive(Debug, Clone)] -pub struct ValidatorTarget { - pubkey: EDDSAPublicKeyTarget, - signature: EDDSASignatureTarget, - message: ValidatorMessageTarget, - message_byte_length: U32Target, - voting_power: I64Target, - validator_byte_length: U32Target, - enabled: BoolTarget, - signed: BoolTarget, -} - -/// The protobuf-encoded leaf (a hash), and it's corresponding proof and path indices against the header. -#[derive(Debug, Clone)] -pub struct InclusionProofTarget { - enc_leaf: EncTendermintHashTarget, - // Path and proof should have a fixed length of HEADER_PROOF_DEPTH. - path: Vec, - proof: Vec, -} - -#[derive(Debug, Clone)] -pub struct CelestiaBlockProofTarget { - validators: Vec>, - header: TendermintHashTarget, - data_hash_proof: InclusionProofTarget, - validator_hash_proof: InclusionProofTarget, - next_validators_hash_proof: InclusionProofTarget, - round_present: BoolTarget, -} +use crate::utils::{ + EncTendermintHashTarget, I64Target, MarshalledValidatorTarget, TendermintHashTarget, + HASH_SIZE_BITS, NUM_POSSIBLE_VALIDATOR_BYTE_LENGTHS, PROTOBUF_HASH_SIZE_BITS, + VALIDATOR_BYTE_LENGTH_MAX, VALIDATOR_BYTE_LENGTH_MIN, VALIDATOR_SET_SIZE_MAX, + VOTING_POWER_BITS_LENGTH_MAX, VOTING_POWER_BYTES_LENGTH_MAX, +}; pub trait TendermintMarshaller, const D: usize> { type Curve: Curve; - // Extract a hash from a protobuf-encoded hash. - fn extract_hash_from_protobuf( - &mut self, - hash: &EncTendermintHashTarget, - ) -> TendermintHashTarget; - /// Serializes an int64 as a protobuf varint. fn marshal_int64_varint( &mut self, @@ -146,15 +39,6 @@ pub trait TendermintMarshaller, const D: usize> { voting_power: &I64Target, ) -> MarshalledValidatorTarget; - /// Extract the header hash from the signed message from a validator. - fn verify_hash_in_message( - &mut self, - message: &ValidatorMessageTarget, - header_hash: &TendermintHashTarget, - // Should be the same for all validators - round_present_in_message: &BoolTarget, - ) -> TendermintHashTarget; - /// Verify a merkle proof against the specified root hash. /// Note: This function will only work for leaves with a length of 34 bytes (protobuf-encoded SHA256 hash) /// Output is the merkle root @@ -207,71 +91,6 @@ pub trait TendermintMarshaller, const D: usize> { validator_byte_length: &Vec, validator_enabled: &Vec, ) -> TendermintHashTarget; - - fn mul_i64_by_u32(&mut self, a: &I64Target, b: U32Target) -> I64Target; - - // Returns a >= b - fn is_i64_gte(&mut self, a: &I64Target, b: &I64Target) -> BoolTarget; - - // Gets the total voting power by summing the voting power of all validators. - fn get_total_voting_power(&mut self, validator_voting_power: &Vec) -> I64Target; - - // Checks if accumulated voting power * m > total voting power * n (threshold is n/m) - fn voting_power_greater_than_threshold( - &mut self, - accumulated_power: &I64Target, - total_voting_power: &I64Target, - threshold_numerator: &U32Target, - threshold_denominator: &U32Target, - ) -> BoolTarget; - - /// Accumulate voting power from the enabled validators & check that the voting power is greater than 2/3 of the total voting power. - fn check_voting_power( - &mut self, - validator_voting_power: &Vec, - validator_enabled: &Vec, - total_voting_power: &I64Target, - threshold_numerator: &U32Target, - threshold_denominator: &U32Target, - ) -> BoolTarget; - - /// Verifies the signatures of the validators in the validator set. - fn verify_signatures< - E: CubicParameters, - C: GenericConfig + 'static, - >( - &mut self, - // This message should be range-checked before being passed in. - messages: Vec>, - eddsa_sig_targets: Vec<&EDDSASignatureTarget>, - eddsa_pubkey_targets: Vec<&EDDSAPublicKeyTarget>, - ) where - >::Hasher: AlgebraicHasher; - - /// Verifies a single signature of a Tendermint validator. - fn verify_signature< - E: CubicParameters, - C: GenericConfig + 'static, - >( - &mut self, - // This message should be range-checked before being passed in. - message: Vec, - eddsa_sig_target: &EDDSASignatureTarget, - eddsa_pubkey_target: &EDDSAPublicKeyTarget, - ) where - >::Hasher: AlgebraicHasher; - - /// Verifies a Tendermint consensus block. - fn step, C: GenericConfig + 'static>( - &mut self, - validators: &Vec>, - header: &TendermintHashTarget, - data_hash_proof: &InclusionProofTarget, - validator_hash_proof: &InclusionProofTarget, - next_validators_hash_proof: &InclusionProofTarget, - round_present: &BoolTarget, - ) where - >::Hasher: AlgebraicHasher; } impl, const D: usize> TendermintMarshaller @@ -279,18 +98,6 @@ impl, const D: usize> TendermintMarshaller { type Curve = Ed25519; - fn extract_hash_from_protobuf( - &mut self, - hash: &EncTendermintHashTarget, - ) -> TendermintHashTarget { - let mut result = [self._false(); HASH_SIZE_BITS]; - // Skip first 2 bytes - for i in 0..HASH_SIZE_BITS { - result[i] = hash.0[i + (8 * 2)]; - } - TendermintHashTarget(result) - } - fn get_root_from_merkle_proof( &mut self, aunts: &Vec, @@ -348,49 +155,6 @@ impl, const D: usize> TendermintMarshaller TendermintHashTarget(return_hash) } - fn verify_hash_in_message( - &mut self, - message: &ValidatorMessageTarget, - header_hash: &TendermintHashTarget, - // Should be the same for all validators - round_present_in_message: &BoolTarget, - ) -> TendermintHashTarget { - // Logic: - // Verify that header_hash is equal to the hash in the message at the correct index. - // If the round is missing, then the hash starts at index 16. - // If the round is present, then the hash starts at index 25. - - let missing_round_start_idx = 16; - - let including_round_start_idx = 25; - - let one = self.one(); - - let mut vec_round_missing = [self._false(); HASH_SIZE_BITS]; - - let mut vec_round_present = [self._false(); HASH_SIZE_BITS]; - - for i in 0..HASH_SIZE_BITS { - vec_round_missing[i] = message.0[(missing_round_start_idx) * 8 + i]; - vec_round_present[i] = message.0[(including_round_start_idx) * 8 + i]; - let round_missing_eq = - self.is_equal(header_hash.0[i].target, vec_round_missing[i].target); - let round_present_eq = - self.is_equal(header_hash.0[i].target, vec_round_present[i].target); - - // Pick the correct bit based on whether the round is present or not. - let hash_eq = self.select( - *round_present_in_message, - round_present_eq.target, - round_missing_eq.target, - ); - - self.connect(hash_eq, one); - } - - *header_hash - } - fn marshal_int64_varint( &mut self, voting_power: &I64Target, @@ -760,444 +524,6 @@ impl, const D: usize> TendermintMarshaller // Return the root hash. current_validator_hashes[0] } - - fn mul_i64_by_u32(&mut self, a: &I64Target, b: U32Target) -> I64Target { - // Multiply the lower 32 bits of the accumulated voting power by b - let (lower_product, lower_carry) = self.mul_u32(a.0[0], b); - - // Multiply the upper 32 bits of the accumulated voting power by b - let (upper_product, upper_carry) = self.mul_u32(a.0[1], b); - - // NOTE: This will limit the maximum size of numbers to (2^64 - 1) / b - self.assert_zero_u32(upper_carry); - - // Add the carry from the lower 32 bits of the accumulated voting power to the upper 32 bits of the accumulated voting power - let (upper_sum, upper_carry) = self.add_u32(upper_product, lower_carry); - - // Check that we did not overflow when multiplying the upper bits - self.assert_zero_u32(upper_carry); - - I64Target([lower_product, upper_sum]) - } - - // Returns a >= b - fn is_i64_gte(&mut self, a: &I64Target, b: &I64Target) -> BoolTarget { - // Check that the a >= b - // 1) a_high > b_high => TRUE - // 2) a_high == b_high - // a) a_low >= b_low => TRUE - // b) a_low < b_low => FAIL - // 3) a_high < b_high => FAIL - - let zero_u32 = self.constant_u32(0); - - let (result_high, underflow_high) = self.sub_u32(a.0[1], b.0[1], zero_u32); - - let no_underflow_high = self.is_equal(underflow_high.0, zero_u32.0); - - // Check if upper 32 bits are equal (a_high - b_high = 0) - let upper_equal = self.is_equal(result_high.0, zero_u32.0); - - let upper_not_equal = self.not(upper_equal); - - // Underflows if a_low < b_low - let (_, underflow_low) = self.sub_u32(a.0[0], b.0[0], zero_u32); - - let no_underflow_low = self.is_equal(underflow_low.0, zero_u32.0); - - // Case 1) - // If there was no underflow & a_high - b_high is not equal (i.e. positive), accumulated voting power is greater. - let upper_pass = self.and(upper_not_equal, no_underflow_high); - - // Case 2a) - // If a_high = b_high & a_low >= b_low, accumulated voting power is greater. - let lower_pass = self.and(upper_equal, no_underflow_low); - - // Note: True if accumulated voting power is >= than 2/3 of the total voting power. - self.or(upper_pass, lower_pass) - } - - fn get_total_voting_power(&mut self, validator_voting_power: &Vec) -> I64Target { - // Sum up the voting power of all the validators - - // Get a vector of the first element of each validator's voting power using a map and collect - let mut validator_voting_power_first = Vec::new(); - for i in 0..VALIDATOR_SET_SIZE_MAX { - validator_voting_power_first.push(validator_voting_power[i].0[0]); - } - - let (sum_lower_low, sum_lower_high) = self.add_many_u32(&mut validator_voting_power_first); - - let mut validator_voting_power_second = Vec::new(); - for i in 0..VALIDATOR_SET_SIZE_MAX { - validator_voting_power_second.push(validator_voting_power[i].0[1]); - } - let (sum_upper_low, sum_upper_high) = self.add_many_u32(&mut validator_voting_power_second); - - self.assert_zero_u32(sum_upper_high); - - let (carry_sum_low, carry_sum_high) = self.add_u32(sum_lower_high, sum_upper_low); - - self.assert_zero_u32(carry_sum_high); - - I64Target([sum_lower_low, carry_sum_low]) - } - - fn voting_power_greater_than_threshold( - &mut self, - accumulated_power: &I64Target, - total_voting_power: &I64Target, - threshold_numerator: &U32Target, - threshold_denominator: &U32Target, - ) -> BoolTarget { - // Threshold is numerator/denominator * total_voting_power - - // Compute accumulated_voting_power * m - let scaled_accumulated_vp = self.mul_i64_by_u32(accumulated_power, *threshold_denominator); - - // Compute total_vp * n - let scaled_total_vp = self.mul_i64_by_u32(total_voting_power, *threshold_numerator); - - self.is_i64_gte(&scaled_accumulated_vp, &scaled_total_vp) - } - - fn check_voting_power( - &mut self, - validator_voting_power: &Vec, - validator_enabled: &Vec, - total_voting_power: &I64Target, - threshold_numerator: &U32Target, - threshold_denominator: &U32Target, - ) -> BoolTarget { - // Accumulate the voting power from the enabled validators. - let mut accumulated_voting_power = - I64Target([U32Target(self.zero()), U32Target(self.zero())]); - for i in 0..VALIDATOR_SET_SIZE_MAX { - let voting_power = validator_voting_power[i]; - let enabled = validator_enabled[i]; - - // Note: Tendermint validators max voting power is 2^63 - 1. (Should below 2^32) - let (sum_lower_low, sum_lower_high) = - self.mul_add_u32(voting_power.0[0], enabled, accumulated_voting_power.0[0]); - - let (carry_sum_low, carry_sum_high) = self.add_u32(sum_lower_high, voting_power.0[1]); - - // This should not overflow from carrying voting_power[1] + accumulated_voting_power[0] - self.assert_zero_u32(carry_sum_high); - - // This should not overflow - let (sum_upper_low, sum_upper_high) = - self.mul_add_u32(carry_sum_low, enabled, accumulated_voting_power.0[1]); - - // Check that the upper 32 bits of the upper sum are zero. - self.assert_zero_u32(sum_upper_high); - - accumulated_voting_power.0[0] = sum_lower_low; - accumulated_voting_power.0[1] = sum_upper_low; - } - - // Note: Because the threshold is n/m, max I64 should be range checked to be < 2^63 / m - self.voting_power_greater_than_threshold( - &accumulated_voting_power, - total_voting_power, - threshold_numerator, - threshold_denominator, - ) - } - - /// Verifies the signatures of the validators in the validator set. - fn verify_signatures< - E: CubicParameters, - C: GenericConfig + 'static, - >( - &mut self, - // This message should be range-checked before being passed in. - messages: Vec>, - eddsa_sig_targets: Vec<&EDDSASignatureTarget>, - eddsa_pubkey_targets: Vec<&EDDSAPublicKeyTarget>, - ) where - >::Hasher: AlgebraicHasher, - { - // TODO: UPDATE message.len() to VALIDATOR_SET_SIZE_MAX - assert!( - messages.len() == eddsa_sig_targets.len() - && messages.len() == eddsa_pubkey_targets.len(), - ); - - // Already in bits - let byte_len = (messages[0].len() / 8) as u128; - - let eddsa_target = - verify_signatures_circuit::(self, messages.len(), byte_len); - - println!("Number of message targets: {:?}", eddsa_target.msgs.len()); - println!("Message target length: {:?}", eddsa_target.msgs[0].len()); - println!("Number of signature targets: {:?}", eddsa_target.sigs.len()); - println!( - "Number of pubkey targets: {:?}", - eddsa_target.pub_keys.len() - ); - - for i in 0..messages.len() { - let message = &messages[i]; - let eddsa_sig_target = eddsa_sig_targets[i]; - let eddsa_pubkey_target = eddsa_pubkey_targets[i]; - for j in 0..8 * VALIDATOR_MESSAGE_BYTES_LENGTH_MAX { - self.connect(eddsa_target.msgs[i][j].target, message[j].target); - } - - self.connect_nonnative(&eddsa_target.sigs[i].s, &eddsa_sig_target.s); - self.connect_nonnative(&eddsa_target.sigs[i].r.x, &eddsa_sig_target.r.x); - self.connect_nonnative(&eddsa_target.sigs[i].r.y, &eddsa_sig_target.r.y); - - self.connect_affine_point(&eddsa_target.pub_keys[i].0, &eddsa_pubkey_target.0); - } - } - - /// Verifies the signatures of the validators in the validator set. - fn verify_signature< - E: CubicParameters, - C: GenericConfig + 'static, - >( - &mut self, - // This should be the messaged signed by the validator that the header hash is extracted from. - // We should range check this outside of the circuit. - message: Vec, - eddsa_sig_target: &EDDSASignatureTarget, - eddsa_pubkey_target: &EDDSAPublicKeyTarget, - ) where - >::Hasher: AlgebraicHasher, - { - let message_bytes_len: usize = message.len() / 8; - let eddsa_target = verify_signatures_circuit::( - self, - 1, - message_bytes_len as u128, - ); - - for i in 0..message.len() { - self.connect(eddsa_target.msgs[0][i].target, message[i].target); - } - - self.connect_nonnative(&eddsa_target.sigs[0].s, &eddsa_sig_target.s); - self.connect_nonnative(&eddsa_target.sigs[0].r.x, &eddsa_sig_target.r.x); - self.connect_nonnative(&eddsa_target.sigs[0].r.y, &eddsa_sig_target.r.y); - - self.connect_affine_point(&eddsa_target.pub_keys[0].0, &eddsa_pubkey_target.0); - } - - fn step, C: GenericConfig + 'static>( - &mut self, - validators: &Vec>, - header: &TendermintHashTarget, - data_hash_proof: &InclusionProofTarget, - validator_hash_proof: &InclusionProofTarget, - next_validators_hash_proof: &InclusionProofTarget, - round_present: &BoolTarget, - ) where - >::Hasher: AlgebraicHasher, - { - let one = self.one(); - // Verify each of the validators marshal correctly - // Assumes the validators are sorted in the correct order - let byte_lengths: Vec = - validators.iter().map(|v| v.validator_byte_length).collect(); - let marshalled_validators: Vec = validators - .iter() - .map(|v| self.marshal_tendermint_validator(&v.pubkey.0, &v.voting_power)) - .collect(); - let validators_enabled: Vec = validators.iter().map(|v| v.enabled).collect(); - let validators_enabled_u32: Vec = validators_enabled - .iter() - .map(|v| { - let zero = self.zero_u32(); - let one = self.one_u32(); - U32Target(self.select(*v, one.0, zero.0)) - }) - .collect(); - - let validator_voting_power: Vec = - validators.iter().map(|v| v.voting_power).collect(); - - let messages: Vec> = - validators.iter().map(|v| v.message.0.to_vec()).collect(); - let signatures: Vec<&EDDSASignatureTarget> = - validators.iter().map(|v| &v.signature).collect(); - let pubkeys: Vec<&EDDSAPublicKeyTarget> = - validators.iter().map(|v| &v.pubkey).collect(); - - // // Compute the validators hash - let validators_hash_target = - self.hash_validator_set(&marshalled_validators, &byte_lengths, &validators_enabled); - - // // Assert that computed validator hash matches expected validator hash - let extracted_hash = self.extract_hash_from_protobuf(&validator_hash_proof.enc_leaf); - for i in 0..HASH_SIZE_BITS { - self.connect( - validators_hash_target.0[i].target, - extracted_hash.0[i].target, - ); - } - - let total_voting_power = self.get_total_voting_power(&validator_voting_power); - let threshold_numerator = self.constant_u32(2); - let threshold_denominator = self.constant_u32(3); - - // Assert the accumulated voting power is greater than the threshold - let check_voting_power_bool = self.check_voting_power( - &validator_voting_power, - &validators_enabled_u32, - &total_voting_power, - &threshold_numerator, - &threshold_denominator, - ); - self.connect(check_voting_power_bool.target, one); - - // TODO: Handle dummies - self.verify_signatures::(messages, signatures, pubkeys); - - // TODO: Verify that this will work with dummy signatures - for i in 0..VALIDATOR_SET_SIZE_MAX { - // Verify that the header is in the message in the correct location - self.verify_hash_in_message(&validators[i].message, header, round_present); - } - - let header_from_data_root_proof = self.get_root_from_merkle_proof( - &data_hash_proof.proof, - &data_hash_proof.path, - &data_hash_proof.enc_leaf, - ); - let header_from_validator_root_proof = self.get_root_from_merkle_proof( - &validator_hash_proof.proof, - &validator_hash_proof.path, - &validator_hash_proof.enc_leaf, - ); - let header_from_next_validators_root_proof = self.get_root_from_merkle_proof( - &next_validators_hash_proof.proof, - &next_validators_hash_proof.path, - &next_validators_hash_proof.enc_leaf, - ); - - // Confirm that the header from the proof of {validator_hash, next_validators_hash, data_hash} all match the header - for i in 0..HASH_SIZE_BITS { - self.connect(header.0[i].target, header_from_data_root_proof.0[i].target); - self.connect( - header.0[i].target, - header_from_validator_root_proof.0[i].target, - ); - self.connect( - header.0[i].target, - header_from_next_validators_root_proof.0[i].target, - ); - } - } -} - -fn create_virtual_bool_target_array, const D: usize>( - builder: &mut CircuitBuilder, - size: usize, -) -> Vec { - let mut result = Vec::new(); - for _i in 0..size { - result.push(builder.add_virtual_bool_target_safe()); - } - result -} - -fn create_virtual_inclusion_proof_target, const D: usize>( - builder: &mut CircuitBuilder, -) -> InclusionProofTarget { - let mut proof = Vec::new(); - for _i in 0..HEADER_PROOF_DEPTH { - proof.push(TendermintHashTarget( - create_virtual_bool_target_array(builder, HASH_SIZE_BITS) - .try_into() - .unwrap(), - )); - } - InclusionProofTarget { - enc_leaf: EncTendermintHashTarget( - create_virtual_bool_target_array(builder, PROTOBUF_HASH_SIZE_BITS) - .try_into() - .unwrap(), - ), - path: create_virtual_bool_target_array(builder, HEADER_PROOF_DEPTH), - proof, - } -} - -pub fn make_step_circuit< - F: RichField + Extendable, - const D: usize, - C: Curve, - Config: GenericConfig + 'static, - E: CubicParameters, ->( - builder: &mut CircuitBuilder, -) -> CelestiaBlockProofTarget -where - Config::Hasher: AlgebraicHasher, -{ - type Curve = Ed25519; - let mut validators = Vec::new(); - for _i in 0..VALIDATOR_SET_SIZE_MAX { - let pubkey = EDDSAPublicKeyTarget(builder.add_virtual_affine_point_target()); - let signature = EDDSASignatureTarget { - r: builder.add_virtual_affine_point_target(), - s: builder.add_virtual_nonnative_target(), - }; - let message = - create_virtual_bool_target_array(builder, VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8); - let message = ValidatorMessageTarget(message.try_into().unwrap()); - - let message_byte_length = builder.add_virtual_u32_target(); - - let voting_power = I64Target([ - builder.add_virtual_u32_target(), - builder.add_virtual_u32_target(), - ]); - let validator_byte_length = builder.add_virtual_u32_target(); - let enabled = builder.add_virtual_bool_target_safe(); - let signed = builder.add_virtual_bool_target_safe(); - - validators.push(ValidatorTarget:: { - pubkey, - signature, - message, - message_byte_length, - voting_power, - validator_byte_length, - enabled, - signed, - }) - } - - let header = create_virtual_bool_target_array(builder, HASH_SIZE_BITS); - let header = TendermintHashTarget(header.try_into().unwrap()); - - let data_hash_proof = create_virtual_inclusion_proof_target(builder); - let validator_hash_proof = create_virtual_inclusion_proof_target(builder); - let next_validators_hash_proof = create_virtual_inclusion_proof_target(builder); - - let round_present = builder.add_virtual_bool_target_safe(); - - builder.step::( - &validators, - &header, - &data_hash_proof, - &validator_hash_proof, - &next_validators_hash_proof, - &round_present, - ); - - CelestiaBlockProofTarget:: { - validators, - header, - data_hash_proof, - validator_hash_proof, - next_validators_hash_proof, - round_present, - } } #[cfg(test)] @@ -1216,28 +542,21 @@ pub(crate) mod tests { config::{GenericConfig, PoseidonGoldilocksConfig}, }, }; - use plonky2x::ecc::ed25519::curve::eddsa::{verify_message, EDDSAPublicKey, EDDSASignature}; - use plonky2x::ecc::ed25519::gadgets::curve::WitnessAffinePoint; - use plonky2x::num::biguint::WitnessBigUint; - use plonky2x::num::u32::witness::WitnessU32; use subtle_encoding::hex; - use plonky2x::num::biguint::CircuitBuilderBiguint; - use plonky2x::ecc::ed25519::curve::curve_types::AffinePoint; - use plonky2x::ecc::ed25519::field::ed25519_scalar::Ed25519Scalar; use sha2::Sha256; use tendermint_proto::Protobuf; - use crate::inputs::{generate_step_inputs, CelestiaBlockProof}; - use crate::validator::{VALIDATOR_BIT_LENGTH_MAX, VALIDATOR_SET_SIZE_MAX}; + use crate::utils::{VALIDATOR_BIT_LENGTH_MAX, VALIDATOR_SET_SIZE_MAX}; - use crate::merkle::{generate_proofs_from_header, hash_all_leaves, leaf_hash}; + use crate::utils::{generate_proofs_from_header, hash_all_leaves, leaf_hash}; use plonky2x::num::u32::gadgets::arithmetic_u32::U32Target; use crate::{ - utils::f_bits_to_bytes, + signature::TendermintSignature, + utils::{f_bits_to_bytes, to_be_bits}, validator::{I64Target, TendermintMarshaller}, }; @@ -1248,40 +567,10 @@ pub(crate) mod tests { type Curve = Ed25519; const D: usize = 2; - fn to_be_bits(msg: Vec) -> Vec { - let mut res = Vec::new(); - for i in 0..msg.len() { - let char = msg[i]; - for j in 0..8 { - if (char & (1 << 7 - j)) != 0 { - res.push(true); - } else { - res.push(false); - } - } - } - res - } - - fn to_le_bits(msg: Vec) -> Vec { - let mut res = Vec::new(); - for i in 0..msg.len() { - let char = msg[i]; - for j in 0..8 { - if (char & (1 << j)) != 0 { - res.push(true); - } else { - res.push(false); - } - } - } - res - } - // Generate the inputs from the validator byte arrays. fn generate_inputs( builder: &mut CircuitBuilder, - validators: &Vec<&str>, + validators: &Vec, ) -> ( Vec, Vec, @@ -1306,7 +595,7 @@ pub(crate) mod tests { // Convert the hex strings to bytes. for i in 0..validators.len() { let val_byte_length = validators[i].len() / 2; - validator_bits[i] = to_be_bits(hex::decode(validators[i]).unwrap()); + validator_bits[i] = to_be_bits(hex::decode(&validators[i]).unwrap()); for j in 0..(val_byte_length * 8) { if validator_bits[i][j] { validators_target[i].0[j] = builder._true(); @@ -1386,9 +675,7 @@ pub(crate) mod tests { let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - let (root_hash, proofs) = generate_proofs_from_header(&block.header); - - println!("root_hash: {:?}", String::from_utf8(hex::encode(root_hash))); + let (_, proofs) = generate_proofs_from_header(&block.header); // Can test with leaf_index 6, 7 or 8 (data_hash, validators_hash, next_validators_hash) let leaf_index = 8; @@ -1398,10 +685,6 @@ pub(crate) mod tests { // let leaf = block.header.validators_hash.encode_vec(); let leaf = block.header.next_validators_hash.encode_vec(); - println!( - "encoded leaf: {:?}", - String::from_utf8(hex::encode(leaf.clone())) - ); let leaf_bits = to_be_bits(leaf); let mut path_indices = vec![]; @@ -1473,10 +756,10 @@ pub(crate) mod tests { let expected_digest = "84f633a570a987326947aafd434ae37f151e98d5e6d429137a4cc378d4a7988e"; let digest_bits = to_be_bits(hex::decode(expected_digest).unwrap()); - let validators: Vec<&str> = vec![ - "de6ad0941095ada2a7996e6a888581928203b8b69e07ee254d289f5b9c9caea193c2ab01902d", - "92fbe0c52937d80c5ea643c7832620b84bfdf154ec7129b8b471a63a763f2fe955af1ac65fd3", - "e902f88b2371ff6243bf4b0ebe8f46205e00749dd4dad07b2ea34350a1f9ceedb7620ab913c2", + let validators: Vec = vec![ + String::from("de6ad0941095ada2a7996e6a888581928203b8b69e07ee254d289f5b9c9caea193c2ab01902d"), + String::from("92fbe0c52937d80c5ea643c7832620b84bfdf154ec7129b8b471a63a763f2fe955af1ac65fd3"), + String::from("e902f88b2371ff6243bf4b0ebe8f46205e00749dd4dad07b2ea34350a1f9ceedb7620ab913c2"), ]; let (validators_target, validator_byte_length, _) = @@ -1509,6 +792,11 @@ pub(crate) mod tests { let validators: Vec<&str> = vec!["6694200ba0e084f7184255abedc39af04463a4ff11e0e0c1326b1b82ea1de50c6b35cf6efa8f7ed3", "739d312e54353379a852b43de497ca4ec52bb49f59b7294a4d6cf19dd648e16cb530b7a7a1e35875d4ab4d90", "4277f2f871f3e041bcd4643c0cf18e5a931c2bfe121ce8983329a289a2b0d2161745a2ddf99bade9a1"]; + let validators = validators + .iter() + .map(|x| String::from(*x)) + .collect::>(); + let validators_bytes: Vec> = validators .iter() .map(|x| hex::decode(x).unwrap()) @@ -1552,184 +840,58 @@ pub(crate) mod tests { } #[test] - fn test_generate_val_hash_normal() { - let mut pw = PartialWitness::new(); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); + fn test_generate_val_hash() { + struct TestCase { + validators: Vec, + expected_digest: String, + } + // Validators from block 11000 on Celestia mocha-3 testnet encoded as bytes. - let validators: Vec<&str> = vec![ + let validators_arr: Vec> = vec![vec![ "0a220a20de25aec935b10f657b43fa97e5a8d4e523bdb0f9972605f0b064eff7b17048ba10aa8d06", "0a220a208de6ad1a569a223e7bb0dade194abb9487221210e1fa8154bf654a10fe6158a610aa8d06", "0a220a20e9b7638ca1c42da37d728970632fda77ec61dcc520395ab5d3a645b9c2b8e8b1100a", "0a220a20bd60452e7f056b22248105e7fd298961371da0d9332ef65fa81691bf51b2e5051001", - ]; - - let (validators_target, validator_byte_length, validator_enabled) = - generate_inputs(&mut builder, &validators); - - let validator_hash_enc_leaf = - "0a20bb5b8b1239565451dcd5ab52b47c26032016cdf1ef2d2115ff104dc9dde3988c"; - let enc_leaf_bits = - to_be_bits(hex::decode(validator_hash_enc_leaf.to_lowercase().as_bytes()).unwrap()); - - let expected_digest = - String::from("BB5B8B1239565451DCD5AB52B47C26032016CDF1EF2D2115FF104DC9DDE3988C") - .to_lowercase(); - let digest_bits = to_be_bits(hex::decode(expected_digest.as_bytes()).unwrap()); - - println!( - "Expected Val Hash: {:?}", - String::from_utf8(hex::encode( - hex::decode(expected_digest.as_bytes()).unwrap() - )) - ); + ], vec!["364db94241a02b701d0dc85ac016fab2366fba326178e6f11d8294931969072b7441fd6b0ff5129d6867", "6fa0cef8f328eb8e2aef2084599662b1ee0595d842058966166029e96bd263e5367185f19af67b099645ec08aa"]]; - let result = builder.hash_validator_set( - &validators_target, - &validator_byte_length, - &validator_enabled, - ); + let digest_arr: Vec<&str> = vec!["BB5B8B1239565451DCD5AB52B47C26032016CDF1EF2D2115FF104DC9DDE3988C", "be110ff9abb6bdeaebf48ac8e179a76fda1f6eaef0150ca6159587f489722204"]; - let enc_leaf_target = enc_leaf_bits + let test_cases: Vec = validators_arr .iter() - .map(|b| builder.constant_bool(*b)) - .collect::>(); - - let extracted_val_hash = builder.extract_hash_from_protobuf(&EncTendermintHashTarget( - enc_leaf_target.try_into().unwrap(), - )); - - for i in 0..HASH_SIZE_BITS { - pw.set_bool_target(result.0[i], digest_bits[i]); - builder.connect(extracted_val_hash.0[i].target, result.0[i].target); - } - - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - println!("Created proof"); - - data.verify(proof).unwrap(); - - println!("Verified proof"); - } - - #[test] - fn test_generate_val_hash_small() { - // Generate the val hash for a small number of validators (would fit in a tree of less than max depth) - let mut pw = PartialWitness::new(); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - // Generated array with byte arrays with variable length [38, 46] bytes (to mimic validator bytes), and computed the validator hash corresponding to a merkle tree of depth 2 formed by these validator bytes. - let validators: Vec<&str> = vec!["364db94241a02b701d0dc85ac016fab2366fba326178e6f11d8294931969072b7441fd6b0ff5129d6867", "6fa0cef8f328eb8e2aef2084599662b1ee0595d842058966166029e96bd263e5367185f19af67b099645ec08aa"]; - - let (validators_target, validator_byte_length, validator_enabled) = - generate_inputs(&mut builder, &validators); - - let expected_digest = "be110ff9abb6bdeaebf48ac8e179a76fda1f6eaef0150ca6159587f489722204"; - let digest_bits = to_be_bits(hex::decode(expected_digest).unwrap()); - - println!( - "Expected Val Hash Encoding (Bytes): {:?}", - hex::decode(expected_digest).unwrap() - ); - - let result = builder.hash_validator_set( - &validators_target, - &validator_byte_length, - &validator_enabled, - ); - - for i in 0..HASH_SIZE_BITS { - if digest_bits[i] { - pw.set_target(result.0[i].target, F::ONE); - } else { - pw.set_target(result.0[i].target, F::ZERO); - } - } - - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - println!("Created proof"); - - data.verify(proof).unwrap(); - - println!("Verified proof"); - } - - #[test] - fn test_accumulate_voting_power() { - let test_cases = [ - // voting power, enabled, pass - (vec![10i64, 10i64, 10i64, 10i64], [1, 1, 1, 0], true), - (vec![10i64, 10i64, 10i64, 10i64], [1, 1, 1, 1], true), - ( - vec![4294967296000i64, 4294967296i64, 10i64, 10i64], - [1, 0, 0, 0], - true, - ), - ( - vec![4294967296000i64, 4294967296000i64, 4294967296000i64, 0i64], - [1, 1, 0, 0], - true, - ), - ]; + .zip(digest_arr.iter()) + .map(|(validators, expected_digest)| TestCase { + validators: validators.iter().map(|x| String::from(*x).to_lowercase()).collect(), + expected_digest: String::from(*expected_digest).to_lowercase(), + }) + .collect(); - // These test cases should pass for test_case in test_cases { let mut pw = PartialWitness::new(); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - let mut all_validators = vec![]; - let mut validators_enabled = vec![]; - let mut total_vp = 0; - for i in 0..test_case.0.len() { - let voting_power = test_case.0[i]; - total_vp += voting_power; - let voting_power_lower = voting_power & ((1 << 32) - 1); - let voting_power_upper = voting_power >> 32; - - let voting_power_lower_target = U32Target( - builder.constant(F::from_canonical_usize(voting_power_lower as usize)), - ); - let voting_power_upper_target = U32Target( - builder.constant(F::from_canonical_usize(voting_power_upper as usize)), - ); - let voting_power_target = - I64Target([voting_power_lower_target, voting_power_upper_target]); - - all_validators.push(voting_power_target); - validators_enabled.push(builder.constant_u32(test_case.1[i])); - } - - let total_vp_lower = total_vp & ((1 << 32) - 1); - let total_vp_upper = total_vp >> 32; - - println!("Lower total vp: {:?}", total_vp_lower); - println!("Upper total vp: {:?}", total_vp_upper); + let (validators_target, validator_byte_length, validator_enabled) = + generate_inputs(&mut builder, &test_case.validators); - let total_vp_lower_target = - U32Target(builder.constant(F::from_canonical_usize(total_vp_lower as usize))); - let total_vp_upper_target = - U32Target(builder.constant(F::from_canonical_usize(total_vp_upper as usize))); - let total_vp_target = I64Target([total_vp_lower_target, total_vp_upper_target]); + let digest_bits = to_be_bits(hex::decode(test_case.expected_digest.as_bytes()).unwrap()); - let two_u32 = builder.constant_u32(2); - let three_u32 = builder.constant_u32(3); + println!( + "Expected Val Hash: {:?}", + String::from_utf8(hex::encode( + hex::decode(test_case.expected_digest.as_bytes()).unwrap() + )) + ); - let result = builder.check_voting_power( - &all_validators, - &validators_enabled, - &total_vp_target, - &two_u32, - &three_u32, + let result = builder.hash_validator_set( + &validators_target, + &validator_byte_length, + &validator_enabled, ); - pw.set_bool_target(result, test_case.2); + for i in 0..HASH_SIZE_BITS { + pw.set_bool_target(result.0[i], digest_bits[i]); + } let data = builder.build::(); let proof = data.prove(pw).unwrap(); @@ -1742,59 +904,6 @@ pub(crate) mod tests { } } - #[test] - fn test_verify_hash_in_message() { - // This is a test case generated from block 144094 of Celestia's Mocha testnet - // Block Hash: 8909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c (needs to be lower case) - // Signed Message (from the last validator): 6b080211de3202000000000022480a208909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c12240801122061263df4855e55fcab7aab0a53ee32cf4f29a1101b56de4a9d249d44e4cf96282a0b089dce84a60610ebb7a81932076d6f6368612d33 - // No round exists in present the message that was signed above - - let header_hash = "8909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c"; - let header_bits = to_be_bits(hex::decode(header_hash).unwrap()); - - let signed_message = "6b080211de3202000000000022480a208909e1b73b7d987e95a7541d96ed484c17a4b0411e98ee4b7c890ad21302ff8c12240801122061263df4855e55fcab7aab0a53ee32cf4f29a1101b56de4a9d249d44e4cf96282a0b089dce84a60610ebb7a81932076d6f6368612d33"; - let signed_message_bits = to_be_bits(hex::decode(signed_message).unwrap()); - - let mut pw = PartialWitness::new(); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let zero = builder._false(); - - let mut signed_message_target = [builder._false(); VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8]; - for i in 0..signed_message_bits.len() { - signed_message_target[i] = builder.constant_bool(signed_message_bits[i]); - } - - let mut header_hash_target = [builder._false(); HASH_SIZE_BITS]; - for i in 0..header_bits.len() { - header_hash_target[i] = builder.constant_bool(header_bits[i]); - } - - let result = builder.verify_hash_in_message( - &ValidatorMessageTarget(signed_message_target), - &TendermintHashTarget(header_hash_target), - &zero, - ); - - for i in 0..HASH_SIZE_BITS { - if header_bits[i] { - pw.set_target(result.0[i].target, F::ONE); - } else { - pw.set_target(result.0[i].target, F::ZERO); - } - } - - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - println!("Created proof"); - - data.verify(proof).unwrap(); - - println!("Verified proof"); - } - #[test] fn test_marshal_int64_varint() { // These are test cases generated from `celestia-core`. @@ -1864,14 +973,7 @@ pub(crate) mod tests { #[test] fn test_marshal_tendermint_validator() { - // This is a test cases generated from `celestia-core`. - // - // allZerosPubkey := make(ed25519.PubKey, ed25519.PubKeySize) - // minimumVotingPower := int64(724325643436111) - // minValidator := NewValidator(allZerosPubkey, minimumVotingPower) - // fmt.Println(minValidator.Bytes()) - // - // The tuples hold the form: (voting_power_i64, voting_power_varint_bytes). + // This is a test cases generated from a validator in block 11000 of the mocha-3 testnet. let voting_power_i64 = 100010 as i64; let pubkey = "de25aec935b10f657b43fa97e5a8d4e523bdb0f9972605f0b064eff7b17048ba"; let expected_marshal = @@ -1890,28 +992,21 @@ pub(crate) mod tests { U32Target(builder.constant(F::from_canonical_usize(voting_power_upper as usize))); let voting_power_target = I64Target([voting_power_lower_target, voting_power_upper_target]); - // let virtual_affine_point_target = builder.add_virtual_affine_point_target(); - let pub_key_uncompressed: AffinePoint = AffinePoint::new_from_compressed_point(&hex::decode(pubkey).unwrap()); let pub_key_affine_t = builder.constant_affine_point(pub_key_uncompressed); - // Correct encoding (LE from pub_key) let pub_key = pub_key_uncompressed.compress_point(); + // Convert pub_key to bytes from biguint let pub_key_bytes = pub_key.to_bytes_le(); println!("pub_key: {:?}", pub_key_bytes); println!("expected marshal: {:?}", hex::decode(expected_marshal)); - // pw.set_affine_point_target::(&eddsa_pub_key_target.0, &pub_key_uncompressed); - let result = builder.marshal_tendermint_validator(&pub_key_affine_t, &voting_power_target); - println!("result: {:?}", result.0[32..(32 + 256)].to_vec()); - - // let mut expected_bits: Vec = bytes_to_le_f_bits(&hex::decode(expected_marshal).unwrap().to_vec()); let expected_bits = to_be_bits(hex::decode(expected_marshal).unwrap().to_vec()); // Only check the hash bits @@ -1934,405 +1029,4 @@ pub(crate) mod tests { println!("Verified proof"); } - - #[test] - fn test_verify_eddsa_signature() { - // First signature from block 11000 - let msg = "6c080211f82a00000000000022480a2036f2d954fe1ba37c5036cb3c6b366d0daf68fccbaa370d9490361c51a0a38b61122408011220cddf370e891591c9d912af175c966cd8dfa44b2c517e965416b769eb4b9d5d8d2a0c08f6b097a50610dffbcba90332076d6f6368612d33"; - let pubkey = "de25aec935b10f657b43fa97e5a8d4e523bdb0f9972605f0b064eff7b17048ba"; - let sig = "091576e9e3ad0e5ba661f7398e1adb3976ba647b579b8e4a224d1d02b591ade6aedb94d3bf55d258f089d6413155a57adfd4932418a798c2d68b29850f6fb50b"; - - let msg_bytes = hex::decode(msg).unwrap(); - let pub_key_bytes = hex::decode(pubkey).unwrap(); - let sig_bytes = hex::decode(sig).unwrap(); - - type F = GoldilocksField; - type Curve = Ed25519; - type E = GoldilocksCubicParameters; - type C = PoseidonGoldilocksConfig; - const D: usize = 2; - - let mut pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(CircuitConfig::standard_ecc_config()); - - let msg_bits = to_be_bits(msg_bytes.to_vec()); - let mut msg_bits_target = Vec::new(); - for i in 0..msg_bits.len() { - msg_bits_target.push(builder.constant_bool(msg_bits[i])); - } - - let virtual_affine_point_target = builder.add_virtual_affine_point_target(); - - let pub_key_uncompressed: AffinePoint = - AffinePoint::new_from_compressed_point(&pub_key_bytes); - - let eddsa_pub_key_target = EDDSAPublicKeyTarget(virtual_affine_point_target); - - pw.set_affine_point_target::(&eddsa_pub_key_target.0, &pub_key_uncompressed); - - let sig_r = AffinePoint::new_from_compressed_point(&sig_bytes[0..32]); - assert!(sig_r.is_valid()); - - let sig_s_biguint = BigUint::from_bytes_le(&sig_bytes[32..64]); - let sig_s = Ed25519Scalar::from_noncanonical_biguint(sig_s_biguint.clone()); - let sig = EDDSASignature { r: sig_r, s: sig_s }; - - assert!(verify_message( - &msg_bits, - &sig, - &EDDSAPublicKey(pub_key_uncompressed) - )); - println!("verified signature"); - - let sig_r_target = builder.constant_affine_point(sig_r); - let sig_s_biguint_target = builder.constant_biguint(&sig_s_biguint); - let sig_s_target = builder.biguint_to_nonnative(&sig_s_biguint_target); - - let eddsa_sig_target = EDDSASignatureTarget { - r: sig_r_target, - s: sig_s_target, - }; - - builder.verify_signature::(msg_bits_target, &eddsa_sig_target, &eddsa_pub_key_target); - - let inner_data = builder.build::(); - let inner_proof = inner_data.prove(pw).unwrap(); - inner_data.verify(inner_proof.clone()).unwrap(); - - let mut outer_builder = CircuitBuilder::::new(CircuitConfig::standard_ecc_config()); - let inner_proof_target = outer_builder.add_virtual_proof_with_pis(&inner_data.common); - let inner_verifier_data = - outer_builder.add_virtual_verifier_data(inner_data.common.config.fri_config.cap_height); - outer_builder.verify_proof::( - &inner_proof_target, - &inner_verifier_data, - &inner_data.common, - ); - - let outer_data = outer_builder.build::(); - for gate in outer_data.common.gates.iter() { - println!("ecddsa verify recursive gate: {:?}", gate); - } - - let mut outer_pw = PartialWitness::new(); - outer_pw.set_proof_with_pis_target(&inner_proof_target, &inner_proof); - outer_pw.set_verifier_data_target(&inner_verifier_data, &inner_data.verifier_only); - - let outer_proof = outer_data.prove(outer_pw).unwrap(); - - outer_data - .verify(outer_proof) - .expect("failed to verify proof"); - } - - #[test] - fn test_generate_root_from_step_inputs() { - let mut pw = PartialWitness::new(); - let config = CircuitConfig::standard_ecc_config(); - let mut builder = CircuitBuilder::::new(config); - - type F = GoldilocksField; - type C = PoseidonGoldilocksConfig; - const D: usize = 2; - - let celestia_block_proof: CelestiaBlockProof = generate_step_inputs(); - - let header_bits = to_be_bits(celestia_block_proof.header); - // println!("header bits: {:?}", header_bits); - - let mut data_hash_aunts = Vec::new(); - let mut data_hash_enc_leaf = Vec::new(); - let mut data_hash_path_indices = Vec::new(); - - let mut val_hash_aunts = Vec::new(); - let mut val_hash_enc_leaf = Vec::new(); - let mut val_hash_path_indices = Vec::new(); - - let mut next_val_hash_aunts = Vec::new(); - let mut next_val_hash_enc_leaf = Vec::new(); - let mut next_val_hash_path_indices = Vec::new(); - - // Set the encoded leaf for each of the proofs - let data_hash_enc_leaf_bits = to_be_bits(celestia_block_proof.data_hash_proof.enc_leaf); - let val_hash_enc_leaf_bits = to_be_bits(celestia_block_proof.validator_hash_proof.enc_leaf); - let next_val_hash_enc_leaf_bits = - to_be_bits(celestia_block_proof.next_validators_hash_proof.enc_leaf); - - for i in 0..PROTOBUF_HASH_SIZE_BITS { - data_hash_enc_leaf.push(builder.constant_bool(data_hash_enc_leaf_bits[i])); - - val_hash_enc_leaf.push(builder.constant_bool(val_hash_enc_leaf_bits[i])); - - next_val_hash_enc_leaf.push(builder.constant_bool(next_val_hash_enc_leaf_bits[i])); - } - - for i in 0..HEADER_PROOF_DEPTH { - // Set path indices for each of the proof indices - data_hash_path_indices - .push(builder.constant_bool(celestia_block_proof.data_hash_proof.path[i])); - val_hash_path_indices - .push(builder.constant_bool(celestia_block_proof.validator_hash_proof.path[i])); - next_val_hash_path_indices.push( - builder.constant_bool(celestia_block_proof.next_validators_hash_proof.path[i]), - ); - - let data_hash_aunt_bits = - to_be_bits(celestia_block_proof.data_hash_proof.proof[i].to_vec()); - - let val_hash_aunt_bits = - to_be_bits(celestia_block_proof.validator_hash_proof.proof[i].to_vec()); - - let next_val_aunt_bits = - to_be_bits(celestia_block_proof.next_validators_hash_proof.proof[i].to_vec()); - - data_hash_aunts.push(Vec::new()); - val_hash_aunts.push(Vec::new()); - next_val_hash_aunts.push(Vec::new()); - - // Set aunts for each of the proofs - for j in 0..HASH_SIZE_BITS { - data_hash_aunts[i].push(builder.constant_bool(data_hash_aunt_bits[j])); - val_hash_aunts[i].push(builder.constant_bool(val_hash_aunt_bits[j])); - next_val_hash_aunts[i].push(builder.constant_bool(next_val_aunt_bits[j])); - } - } - - let mut data_hash_aunts_new = Vec::new(); - let mut val_hash_aunts_new = Vec::new(); - let mut next_val_hash_aunts_new = Vec::new(); - for i in 0..HEADER_PROOF_DEPTH { - data_hash_aunts_new.push(TendermintHashTarget( - data_hash_aunts[i].clone().try_into().unwrap(), - )); - val_hash_aunts_new.push(TendermintHashTarget( - val_hash_aunts[i].clone().try_into().unwrap(), - )); - next_val_hash_aunts_new.push(TendermintHashTarget( - next_val_hash_aunts[i].clone().try_into().unwrap(), - )); - } - - let header_from_data_hash = builder.get_root_from_merkle_proof( - &data_hash_aunts_new, - &data_hash_path_indices, - &EncTendermintHashTarget(data_hash_enc_leaf.try_into().unwrap()), - ); - - let header_from_val_hash = builder.get_root_from_merkle_proof( - &val_hash_aunts_new, - &val_hash_path_indices, - &EncTendermintHashTarget(val_hash_enc_leaf.try_into().unwrap()), - ); - - let header_from_next_val_hash = builder.get_root_from_merkle_proof( - &next_val_hash_aunts_new, - &next_val_hash_path_indices, - &EncTendermintHashTarget(next_val_hash_enc_leaf.try_into().unwrap()), - ); - - for i in 0..HASH_SIZE_BITS { - pw.set_bool_target(header_from_data_hash.0[i], header_bits[i]); - pw.set_bool_target(header_from_val_hash.0[i], header_bits[i]); - pw.set_bool_target(header_from_next_val_hash.0[i], header_bits[i]); - } - - let data = builder.build::(); - let proof = data.prove(pw).unwrap(); - - println!("Created proof"); - - data.verify(proof).unwrap(); - - println!("Verified proof"); - } - - #[test] - fn test_step() { - let _ = env_logger::builder().is_test(true).try_init(); - - let mut pw = PartialWitness::new(); - let config = CircuitConfig::standard_ecc_config(); - let mut builder = CircuitBuilder::::new(config); - - type F = GoldilocksField; - type Curve = Ed25519; - type E = GoldilocksCubicParameters; - type C = PoseidonGoldilocksConfig; - const D: usize = 2; - - let celestia_proof_target = - make_step_circuit::(&mut builder); - - let celestia_block_proof: CelestiaBlockProof = generate_step_inputs(); - - // Set target for header - let header_bits = to_be_bits(celestia_block_proof.header); - for i in 0..HASH_SIZE_BITS { - pw.set_bool_target(celestia_proof_target.header.0[i], header_bits[i]); - } - - // Set target for round present - pw.set_bool_target( - celestia_proof_target.round_present, - celestia_block_proof.round_present, - ); - - // Set the encoded leaf for each of the proofs - let data_hash_enc_leaf = to_be_bits(celestia_block_proof.data_hash_proof.enc_leaf); - let val_hash_enc_leaf = to_be_bits(celestia_block_proof.validator_hash_proof.enc_leaf); - let next_val_hash_enc_leaf = - to_be_bits(celestia_block_proof.next_validators_hash_proof.enc_leaf); - - for i in 0..PROTOBUF_HASH_SIZE_BITS { - pw.set_bool_target( - celestia_proof_target.data_hash_proof.enc_leaf.0[i], - data_hash_enc_leaf[i], - ); - pw.set_bool_target( - celestia_proof_target.validator_hash_proof.enc_leaf.0[i], - val_hash_enc_leaf[i], - ); - pw.set_bool_target( - celestia_proof_target.next_validators_hash_proof.enc_leaf.0[i], - next_val_hash_enc_leaf[i], - ); - } - - for i in 0..HEADER_PROOF_DEPTH { - // Set path indices for each of the proof indices - pw.set_bool_target( - celestia_proof_target.data_hash_proof.path[i], - celestia_block_proof.data_hash_proof.path[i], - ); - pw.set_bool_target( - celestia_proof_target.validator_hash_proof.path[i], - celestia_block_proof.validator_hash_proof.path[i], - ); - pw.set_bool_target( - celestia_proof_target.next_validators_hash_proof.path[i], - celestia_block_proof.next_validators_hash_proof.path[i], - ); - - let data_hash_aunt = to_be_bits(celestia_block_proof.data_hash_proof.proof[i].to_vec()); - - let val_hash_aunt = - to_be_bits(celestia_block_proof.validator_hash_proof.proof[i].to_vec()); - - let next_val_aunt = - to_be_bits(celestia_block_proof.next_validators_hash_proof.proof[i].to_vec()); - - // Set aunts for each of the proofs - for j in 0..HASH_SIZE_BITS { - pw.set_bool_target( - celestia_proof_target.data_hash_proof.proof[i].0[j], - data_hash_aunt[j], - ); - pw.set_bool_target( - celestia_proof_target.validator_hash_proof.proof[i].0[j], - val_hash_aunt[j], - ); - pw.set_bool_target( - celestia_proof_target.next_validators_hash_proof.proof[i].0[j], - next_val_aunt[j], - ); - } - } - - // Set the targets for each of the validators - for i in 0..VALIDATOR_SET_SIZE_MAX { - let validator = &celestia_block_proof.validators[i]; - let signature_bytes = validator.signature.clone().into_bytes(); - - let voting_power_lower = (validator.voting_power & ((1 << 32) - 1)) as u32; - let voting_power_upper = (validator.voting_power >> 32) as u32; - - let pub_key_uncompressed: AffinePoint = - AffinePoint::new_from_compressed_point(validator.pubkey.as_bytes()); - - let sig_r: AffinePoint = - AffinePoint::new_from_compressed_point(&signature_bytes[0..32]); - assert!(sig_r.is_valid()); - - let sig_s_biguint = BigUint::from_bytes_le(&signature_bytes[32..64]); - let _sig_s = Ed25519Scalar::from_noncanonical_biguint(sig_s_biguint.clone()); - - // Set the targets for the public key - pw.set_affine_point_target( - &celestia_proof_target.validators[i].pubkey.0, - &pub_key_uncompressed, - ); - - // Set signature targets - pw.set_affine_point_target(&celestia_proof_target.validators[i].signature.r, &sig_r); - pw.set_biguint_target( - &celestia_proof_target.validators[i].signature.s.value, - &sig_s_biguint, - ); - - let message_bits = to_be_bits(validator.message.clone()); - // Set messages for each of the proofs - for j in 0..VALIDATOR_MESSAGE_BYTES_LENGTH_MAX * 8 { - pw.set_bool_target( - celestia_proof_target.validators[i].message.0[j], - message_bits[j], - ); - } - - // Set voting power targets - pw.set_u32_target( - celestia_proof_target.validators[i].voting_power.0[0], - voting_power_lower, - ); - pw.set_u32_target( - celestia_proof_target.validators[i].voting_power.0[1], - voting_power_upper, - ); - - // Set length targets - pw.set_u32_target( - celestia_proof_target.validators[i].validator_byte_length, - validator.validator_byte_length as u32, - ); - pw.set_u32_target( - celestia_proof_target.validators[i].message_byte_length, - validator.message_byte_length as u32, - ); - - // Set enabled and signed - pw.set_bool_target( - celestia_proof_target.validators[i].enabled, - validator.enabled, - ); - pw.set_bool_target(celestia_proof_target.validators[i].signed, validator.signed); - } - - let inner_data = builder.build::(); - let inner_proof = inner_data.prove(pw).unwrap(); - inner_data.verify(inner_proof.clone()).unwrap(); - - // let mut outer_builder = CircuitBuilder::::new(CircuitConfig::standard_ecc_config()); - // let inner_proof_target = outer_builder.add_virtual_proof_with_pis(&inner_data.common); - // let inner_verifier_data = - // outer_builder.add_virtual_verifier_data(inner_data.common.config.fri_config.cap_height); - // outer_builder.verify_proof::( - // &inner_proof_target, - // &inner_verifier_data, - // &inner_data.common, - // ); - - // let outer_data = outer_builder.build::(); - // for gate in outer_data.common.gates.iter() { - // println!("ecddsa verify recursive gate: {:?}", gate); - // } - - // let mut outer_pw = PartialWitness::new(); - // outer_pw.set_proof_with_pis_target(&inner_proof_target, &inner_proof); - // outer_pw.set_verifier_data_target(&inner_verifier_data, &inner_data.verifier_only); - - // let outer_proof = outer_data.prove(outer_pw).unwrap(); - - // outer_data - // .verify(outer_proof) - // .expect("failed to verify proof"); - } } diff --git a/src/voting.rs b/src/voting.rs new file mode 100644 index 00000000..17692e5c --- /dev/null +++ b/src/voting.rs @@ -0,0 +1,299 @@ +//! The protobuf encoding of a Tendermint validator is a deterministic function of the validator's +//! public key (32 bytes) and voting power (int64). The encoding is as follows in bytes: +// +//! 10 34 10 32 16 +// +//! The `pubkey` is encoded as the raw list of bytes used in the public key. The `varint` is +//! encoded using protobuf's default integer encoding, which consist of 7 bit payloads. You can +//! read more about them here: https://protobuf.dev/programming-guides/encoding/#varints. +use plonky2::field::extension::Extendable; +use plonky2::iop::target::BoolTarget; +use plonky2::{hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; +use plonky2x::ecc::ed25519::curve::curve_types::Curve; +use plonky2x::ecc::ed25519::curve::ed25519::Ed25519; +use plonky2x::num::u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target}; + +use crate::utils::{I64Target, VALIDATOR_SET_SIZE_MAX}; + +pub trait TendermintVoting, const D: usize> { + type Curve: Curve; + + fn mul_i64_by_u32(&mut self, a: &I64Target, b: U32Target) -> I64Target; + + // Returns a >= b + fn is_i64_gte(&mut self, a: &I64Target, b: &I64Target) -> BoolTarget; + + // Gets the total voting power by summing the voting power of all validators. + fn get_total_voting_power(&mut self, validator_voting_power: &Vec) -> I64Target; + + // Checks if accumulated voting power * m > total voting power * n (threshold is n/m) + fn voting_power_greater_than_threshold( + &mut self, + accumulated_power: &I64Target, + total_voting_power: &I64Target, + threshold_numerator: &U32Target, + threshold_denominator: &U32Target, + ) -> BoolTarget; + + /// Accumulate voting power from the enabled validators & check that the voting power is greater than 2/3 of the total voting power. + fn check_voting_power( + &mut self, + validator_voting_power: &Vec, + validator_enabled: &Vec, + total_voting_power: &I64Target, + threshold_numerator: &U32Target, + threshold_denominator: &U32Target, + ) -> BoolTarget; +} + +impl, const D: usize> TendermintVoting for CircuitBuilder { + type Curve = Ed25519; + + fn mul_i64_by_u32(&mut self, a: &I64Target, b: U32Target) -> I64Target { + // Multiply the lower 32 bits of the accumulated voting power by b + let (lower_product, lower_carry) = self.mul_u32(a.0[0], b); + + // Multiply the upper 32 bits of the accumulated voting power by b + let (upper_product, upper_carry) = self.mul_u32(a.0[1], b); + + // NOTE: This will limit the maximum size of numbers to (2^64 - 1) / b + self.assert_zero_u32(upper_carry); + + // Add the carry from the lower 32 bits of the accumulated voting power to the upper 32 bits of the accumulated voting power + let (upper_sum, upper_carry) = self.add_u32(upper_product, lower_carry); + + // Check that we did not overflow when multiplying the upper bits + self.assert_zero_u32(upper_carry); + + I64Target([lower_product, upper_sum]) + } + + // Returns a >= b + fn is_i64_gte(&mut self, a: &I64Target, b: &I64Target) -> BoolTarget { + // Check that the a >= b + // 1) a_high > b_high => TRUE + // 2) a_high == b_high + // a) a_low >= b_low => TRUE + // b) a_low < b_low => FAIL + // 3) a_high < b_high => FAIL + + let zero_u32 = self.constant_u32(0); + + let (result_high, underflow_high) = self.sub_u32(a.0[1], b.0[1], zero_u32); + + let no_underflow_high = self.is_equal(underflow_high.0, zero_u32.0); + + // Check if upper 32 bits are equal (a_high - b_high = 0) + let upper_equal = self.is_equal(result_high.0, zero_u32.0); + + let upper_not_equal = self.not(upper_equal); + + // Underflows if a_low < b_low + let (_, underflow_low) = self.sub_u32(a.0[0], b.0[0], zero_u32); + + let no_underflow_low = self.is_equal(underflow_low.0, zero_u32.0); + + // Case 1) + // If there was no underflow & a_high - b_high is not equal (i.e. positive), accumulated voting power is greater. + let upper_pass = self.and(upper_not_equal, no_underflow_high); + + // Case 2a) + // If a_high = b_high & a_low >= b_low, accumulated voting power is greater. + let lower_pass = self.and(upper_equal, no_underflow_low); + + // Note: True if accumulated voting power is >= than 2/3 of the total voting power. + self.or(upper_pass, lower_pass) + } + + fn get_total_voting_power(&mut self, validator_voting_power: &Vec) -> I64Target { + // Sum up the voting power of all the validators + + // Get a vector of the first element of each validator's voting power using a map and collect + let mut validator_voting_power_first = Vec::new(); + for i in 0..VALIDATOR_SET_SIZE_MAX { + validator_voting_power_first.push(validator_voting_power[i].0[0]); + } + + let (sum_lower_low, sum_lower_high) = self.add_many_u32(&mut validator_voting_power_first); + + let mut validator_voting_power_second = Vec::new(); + for i in 0..VALIDATOR_SET_SIZE_MAX { + validator_voting_power_second.push(validator_voting_power[i].0[1]); + } + let (sum_upper_low, sum_upper_high) = self.add_many_u32(&mut validator_voting_power_second); + + self.assert_zero_u32(sum_upper_high); + + let (carry_sum_low, carry_sum_high) = self.add_u32(sum_lower_high, sum_upper_low); + + self.assert_zero_u32(carry_sum_high); + + I64Target([sum_lower_low, carry_sum_low]) + } + + fn voting_power_greater_than_threshold( + &mut self, + accumulated_power: &I64Target, + total_voting_power: &I64Target, + threshold_numerator: &U32Target, + threshold_denominator: &U32Target, + ) -> BoolTarget { + // Threshold is numerator/denominator * total_voting_power + + // Compute accumulated_voting_power * m + let scaled_accumulated_vp = self.mul_i64_by_u32(accumulated_power, *threshold_denominator); + + // Compute total_vp * n + let scaled_total_vp = self.mul_i64_by_u32(total_voting_power, *threshold_numerator); + + self.is_i64_gte(&scaled_accumulated_vp, &scaled_total_vp) + } + + fn check_voting_power( + &mut self, + validator_voting_power: &Vec, + validator_enabled: &Vec, + total_voting_power: &I64Target, + threshold_numerator: &U32Target, + threshold_denominator: &U32Target, + ) -> BoolTarget { + // Accumulate the voting power from the enabled validators. + let mut accumulated_voting_power = + I64Target([U32Target(self.zero()), U32Target(self.zero())]); + for i in 0..VALIDATOR_SET_SIZE_MAX { + let voting_power = validator_voting_power[i]; + let enabled = validator_enabled[i]; + + // Note: Tendermint validators max voting power is 2^63 - 1. (Should below 2^32) + let (sum_lower_low, sum_lower_high) = + self.mul_add_u32(voting_power.0[0], enabled, accumulated_voting_power.0[0]); + + let (carry_sum_low, carry_sum_high) = self.add_u32(sum_lower_high, voting_power.0[1]); + + // This should not overflow from carrying voting_power[1] + accumulated_voting_power[0] + self.assert_zero_u32(carry_sum_high); + + // This should not overflow + let (sum_upper_low, sum_upper_high) = + self.mul_add_u32(carry_sum_low, enabled, accumulated_voting_power.0[1]); + + // Check that the upper 32 bits of the upper sum are zero. + self.assert_zero_u32(sum_upper_high); + + accumulated_voting_power.0[0] = sum_lower_low; + accumulated_voting_power.0[1] = sum_upper_low; + } + + // Note: Because the threshold is n/m, max I64 should be range checked to be < 2^63 / m + self.voting_power_greater_than_threshold( + &accumulated_voting_power, + total_voting_power, + threshold_numerator, + threshold_denominator, + ) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use plonky2::field::types::Field; + use plonky2::{ + iop::witness::{PartialWitness, WitnessWrite}, + plonk::{ + circuit_builder::CircuitBuilder, + circuit_data::CircuitConfig, + config::{GenericConfig, PoseidonGoldilocksConfig}, + }, + }; + + use plonky2x::num::u32::gadgets::arithmetic_u32::U32Target; + + use crate::utils::I64Target; + + type C = PoseidonGoldilocksConfig; + type F = >::F; + const D: usize = 2; + + #[test] + fn test_accumulate_voting_power() { + let test_cases = [ + // voting power, enabled, pass + (vec![10i64, 10i64, 10i64, 10i64], [1, 1, 1, 0], true), + (vec![10i64, 10i64, 10i64, 10i64], [1, 1, 1, 1], true), + ( + vec![4294967296000i64, 4294967296i64, 10i64, 10i64], + [1, 0, 0, 0], + true, + ), + ( + vec![4294967296000i64, 4294967296000i64, 4294967296000i64, 0i64], + [1, 1, 0, 0], + true, + ), + ]; + + // These test cases should pass + for test_case in test_cases { + let mut pw = PartialWitness::new(); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let mut all_validators = vec![]; + let mut validators_enabled = vec![]; + let mut total_vp = 0; + for i in 0..test_case.0.len() { + let voting_power = test_case.0[i]; + total_vp += voting_power; + let voting_power_lower = voting_power & ((1 << 32) - 1); + let voting_power_upper = voting_power >> 32; + + let voting_power_lower_target = U32Target( + builder.constant(F::from_canonical_usize(voting_power_lower as usize)), + ); + let voting_power_upper_target = U32Target( + builder.constant(F::from_canonical_usize(voting_power_upper as usize)), + ); + let voting_power_target = + I64Target([voting_power_lower_target, voting_power_upper_target]); + + all_validators.push(voting_power_target); + validators_enabled.push(builder.constant_u32(test_case.1[i])); + } + + let total_vp_lower = total_vp & ((1 << 32) - 1); + let total_vp_upper = total_vp >> 32; + + println!("Lower total vp: {:?}", total_vp_lower); + println!("Upper total vp: {:?}", total_vp_upper); + + let total_vp_lower_target = + U32Target(builder.constant(F::from_canonical_usize(total_vp_lower as usize))); + let total_vp_upper_target = + U32Target(builder.constant(F::from_canonical_usize(total_vp_upper as usize))); + let total_vp_target = I64Target([total_vp_lower_target, total_vp_upper_target]); + + let two_u32 = builder.constant_u32(2); + let three_u32 = builder.constant_u32(3); + + let result = builder.check_voting_power( + &all_validators, + &validators_enabled, + &total_vp_target, + &two_u32, + &three_u32, + ); + + pw.set_bool_target(result, test_case.2); + + let data = builder.build::(); + let proof = data.prove(pw).unwrap(); + + println!("Created proof"); + + data.verify(proof).unwrap(); + + println!("Verified proof"); + } + } +}