From efdd6857ab7e9b25e6c3e173fc32ca63812ac21c Mon Sep 17 00:00:00 2001 From: Kamen Mladenov Date: Mon, 17 Feb 2025 18:37:08 +0200 Subject: feat(guests): Add dvt-circuits crate This is a port of https://github.com/metacraft-labs/dvt-circuits Co-authored-by: Marto --- guests/dvt-circuits/Cargo.lock | 848 ++++++++++++++++++++++ guests/dvt-circuits/Cargo.toml | 36 + guests/dvt-circuits/bls_utils/Cargo.toml | 15 + guests/dvt-circuits/bls_utils/src/bls.rs | 410 +++++++++++ guests/dvt-circuits/bls_utils/src/lib.rs | 6 + guests/dvt-circuits/bls_utils/src/verification.rs | 338 +++++++++ guests/dvt-circuits/default.env | 0 guests/dvt-circuits/default_private_input.toml | 64 ++ guests/dvt-circuits/default_public_input.toml | 0 guests/dvt-circuits/dvt_abi/Cargo.toml | 11 + guests/dvt-circuits/dvt_abi/src/lib.rs | 208 ++++++ guests/dvt-circuits/src/lib.rs | 18 + 12 files changed, 1954 insertions(+) create mode 100644 guests/dvt-circuits/Cargo.lock create mode 100644 guests/dvt-circuits/Cargo.toml create mode 100644 guests/dvt-circuits/bls_utils/Cargo.toml create mode 100644 guests/dvt-circuits/bls_utils/src/bls.rs create mode 100644 guests/dvt-circuits/bls_utils/src/lib.rs create mode 100644 guests/dvt-circuits/bls_utils/src/verification.rs create mode 100644 guests/dvt-circuits/default.env create mode 100644 guests/dvt-circuits/default_private_input.toml create mode 100644 guests/dvt-circuits/default_public_input.toml create mode 100644 guests/dvt-circuits/dvt_abi/Cargo.toml create mode 100644 guests/dvt-circuits/dvt_abi/src/lib.rs create mode 100644 guests/dvt-circuits/src/lib.rs diff --git a/guests/dvt-circuits/Cargo.lock b/guests/dvt-circuits/Cargo.lock new file mode 100644 index 0000000..c02a82d --- /dev/null +++ b/guests/dvt-circuits/Cargo.lock @@ -0,0 +1,848 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "git+https://github.com/sp1-patches/bls12_381#9ea427c0eb1a7e2ac16902a322aea156c496ddb0" +dependencies = [ + "digest", + "ff", + "group", + "pairing", + "rand_core", + "subtle", +] + +[[package]] +name = "bls_utils" +version = "0.1.0" +dependencies = [ + "bls12_381", + "dvt_abi", + "ff", + "group", + "hex", + "rand", + "serde", + "sha2", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dvt_abi" +version = "0.1.0" +dependencies = [ + "hex", + "serde", + "serde_json", + "validator", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "finalization_prove" +version = "1.1.0" +dependencies = [ + "bls12_381", + "bls_utils", + "dvt_abi", + "ff", + "group", + "guests_macro", + "hex", + "rand", + "serde", + "sha2", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[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 = "guests_macro" +version = "0.1.0" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "validator" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303" +dependencies = [ + "idna", + "once_cell", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac855a2ce6f843beb229757e6e570a42e837bcb15e5f449dd48d5747d41bf77" +dependencies = [ + "darling", + "once_cell", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/guests/dvt-circuits/Cargo.toml b/guests/dvt-circuits/Cargo.toml new file mode 100644 index 0000000..0f01828 --- /dev/null +++ b/guests/dvt-circuits/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "finalization_prove" +version = "1.1.0" +edition = "2021" +publish = false + +[workspace] +members = [ + "bls_utils", + "dvt_abi", +] + +[workspace.package] +edition = "2021" +version = "0.1.0" +authors = ["Martin Dobrev ", "Grigor Gachev", "Zahary Karadjov" ] +keywords = ["nimbus", "dvt"] +categories = ["cryptography"] +repository = "https://github.com/metacraft-labs/dvt-circuits" +license = "MIT OR Apache-2.0" + +[dependencies] +bls12_381 = { git = "https://github.com/sp1-patches/bls12_381", features = ["experimental"] } +ff = "0.13.0" +rand = "0.8.5" +group = "0.13.0" +serde = "1.0.216" +dvt_abi = { path = "./dvt_abi" } +bls_utils = { path = "./bls_utils" } +sha2 = "0.10" +hex = "0.4" + +guests_macro = { version = "0.1.0", path = "../../guests_macro" } + +[features] +no_std = [] diff --git a/guests/dvt-circuits/bls_utils/Cargo.toml b/guests/dvt-circuits/bls_utils/Cargo.toml new file mode 100644 index 0000000..a6f4922 --- /dev/null +++ b/guests/dvt-circuits/bls_utils/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bls_utils" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } + +[dependencies] +bls12_381 = { git = "https://github.com/sp1-patches/bls12_381", features = ["experimental"] } +ff = "0.13.0" +rand = "0.8.5" +group = "0.13.0" +serde = "1.0.216" +dvt_abi = { path = "../dvt_abi" } +sha2 = "0.10" +hex = "0.4" diff --git a/guests/dvt-circuits/bls_utils/src/bls.rs b/guests/dvt-circuits/bls_utils/src/bls.rs new file mode 100644 index 0000000..3a6c7ba --- /dev/null +++ b/guests/dvt-circuits/bls_utils/src/bls.rs @@ -0,0 +1,410 @@ +use bls12_381::{ + hash_to_curve::{ExpandMsgXmd, HashToCurve}, + pairing, G1Affine, G1Projective, G2Affine, G2Projective, Scalar, +}; + +use dvt_abi::{self}; +use sha2::Sha256; +use std::fmt; + +// https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing +// +// In Shamir's secret sharing, a secret is encoded as a n-degree polynomial +// F where the secret value is equal to F(0). Here, F(0) is provided as mask[0]. +// +// A key share is generated by evaluating the polynomial in the `id` point. +// Later we can use at least N of these points to recover the original secret. +// +// Furthermore, if we sign some message M with at least K of the secret key +// shares we can restore from them the signature of the same message signed +// with original secret key. +// +// For a more gentle introductiont to Shamir's secret sharing, see also: +// +// https://github.com/dashpay/dips/blob/master/dip-0006/bls_m-of-n_threshold_scheme_and_dkg.md +// https://medium.com/toruslabs/what-distributed-key-generation-is-866adc79620 +pub fn evaluate_polynomial(cfs: Vec, x: Scalar) -> G1Affine { + let cfst: Vec = cfs.iter().map(|c| G1Projective::from(c)).collect(); + let count = cfst.len(); + if count == 0 { + return G1Affine::identity(); + } else if count == 1 { + return cfs[0]; + } else { + let mut y = cfst[count - 1]; + for i in 2..(count + 1) { + y = y * x + cfs[count - i]; + } + return G1Affine::from(y); + } +} + +pub fn lagrange_interpolation( + y_vec: &Vec, + x_vec: &Vec, +) -> Result> { + let k = x_vec.len(); + if k == 0 || k != y_vec.len() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "invalid inputs", + ))); + } + if k == 1 { + return Ok(y_vec[0]); + } + + // We calculate L(0) so we can simplify + // (X - X0) .. (X - Xj-1) * (X - Xj+1) .. (X - Xk) to just X0 * X1 .. Xk + // Later we can divide by Xi for each basis polynomial li(0) + let mut a = x_vec[0]; + for i in 1..k { + a *= x_vec[i]; + } + if a == Scalar::zero() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "zero secret share id", + ))); + } + let mut r = G1Projective::identity(); + for i in 0..k { + let mut b = x_vec[i]; + for j in 0..k { + if j != i { + let v = x_vec[j] - x_vec[i]; + if v == Scalar::zero() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "duplicate secret share id", + ))); + } + b *= v; + } + } + let li0 = a * b.invert().unwrap(); + let tmp = y_vec[i] * li0; + r = r + tmp; + } + Ok(G1Affine::from(r)) +} + +pub fn hash_message_to_g2(msg: &[u8], domain: &[u8]) -> G2Projective { + >>::hash_to_curve([msg], domain) +} + +pub fn bls_verify(pubkey: &G1Affine, signature: &G2Affine, message: &[u8]) -> bool { + let domain = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + let pk_projective = G1Projective::from(pubkey); + let sig_projective = G2Projective::from(signature); + + let hashed_msg = hash_message_to_g2(message, domain); + let left = pairing(&G1Affine::from(pk_projective), &G2Affine::from(hashed_msg)); + let right = pairing(&G1Affine::generator(), &G2Affine::from(sig_projective)); + + left == right +} + +pub fn bls_id_from_u32(id: u32) -> Scalar { + let unwrapped_le: [u8; 4] = (id as u32).to_le_bytes(); + let mut bytes = [0u8; 32]; + bytes[..4].copy_from_slice(&unwrapped_le); + Scalar::from_bytes(&bytes).unwrap() +} + +#[derive(PartialEq)] +pub struct PublicKey { + key: G1Affine, +} + +#[derive(PartialEq)] +pub struct SecretKey { + key: Scalar, +} + +impl PublicKey { + pub fn to_hex(&self) -> String { + hex::encode(self.key.to_compressed()) + } + + pub fn from_bytes(bytes: &dvt_abi::BLSPubkey) -> Result> { + let g1 = G1Affine::from_compressed(&bytes).into_option(); + match g1 { + Some(g1) => Ok(PublicKey { key: g1 }), + None => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid public key", + ))), + } + } + + pub fn from_g1(g1: &G1Affine) -> PublicKey { + PublicKey { key: *g1 } + } + + pub fn verify_signature(&self, message: &[u8], signature: &dvt_abi::BLSSignature) -> bool { + bls_verify( + &self.key, + &G2Affine::from_compressed(signature).into_option().unwrap(), + message, + ) + } + + pub fn eq(&self, g1: &G1Affine) -> bool { + self.key == *g1 + } +} + +impl SecretKey { + pub fn to_public_key(&self) -> PublicKey { + PublicKey { + key: G1Affine::from(G1Affine::generator() * self.key), + } + } + + pub fn from_bytes(bytes: &[u8; 32]) -> Result> { + let mut le_bytes = bytes.clone(); + le_bytes.reverse(); + + let sk = Scalar::from_bytes(&le_bytes); + + if sk.is_none().into() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid secret key", + ))); + } + + Ok(SecretKey { key: sk.unwrap() }) + } + + pub fn to_bytes(&self) -> [u8; 32] { + self.key.to_bytes() + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PublicKey({})", self.to_hex()) + } +} + +impl fmt::Debug for SecretKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SecretKey({})", hex::encode(self.to_bytes())) + } +} + +#[cfg(test)] +mod tests { + use dvt_abi::{BLSPubkey, BLSSignature}; + + use super::*; + + #[test] + fn test_bls_id_from_u32() { + let mut bytes: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]; + assert_eq!(bls_id_from_u32(0), Scalar::from_bytes(&bytes).unwrap()); + bytes[0] = 1; + assert_eq!(bls_id_from_u32(1), Scalar::from_bytes(&bytes).unwrap()); + bytes[0] = 2; + assert_eq!(bls_id_from_u32(2), Scalar::from_bytes(&bytes).unwrap()); + } + + #[test] + fn test_bls_id_from_u32_to_hex() { + let id = bls_id_from_u32(0); + assert_eq!( + hex::encode(id.to_bytes()), + "0000000000000000000000000000000000000000000000000000000000000000" + ); + let id = bls_id_from_u32(1); + assert_eq!( + hex::encode(id.to_bytes()), + "0100000000000000000000000000000000000000000000000000000000000000" + ); + let id = bls_id_from_u32(2); + assert_eq!( + hex::encode(id.to_bytes()), + "0200000000000000000000000000000000000000000000000000000000000000" + ); + } + + #[test] + fn test_verify_signature() { + let data = hex::decode("2f901d5cec8722e44afd59e94d0a56bf1506a72a0a60709920aad714d1a2ece0") + .unwrap(); + let pk: BLSPubkey = hex::decode("90346f9c5f3c09d96ea02acd0220daa8459f03866ed938c798e3716e42c7e033c9a7ef66a10f83af06d5c00b508c6d0f").unwrap().try_into().unwrap(); + let sig:BLSSignature = hex::decode("a9c08eff13742f78f1e5929888f223b5b5b12b4836b5417c5a135cf24f4e2a4c66a6cdef91be3098b7e7a6a63903b61302e3cf2b8653101da245cf01a8d82b25debe7b18a3a2eb1778f8628fd2c59c8687f6e048a31250fbc2804c20043b8443").unwrap().try_into().unwrap(); + let pk = G1Affine::from_compressed(&pk).into_option().unwrap(); + let sig = G2Affine::from_compressed(&sig).into_option().unwrap(); + assert!(bls_verify(&pk, &sig, &data)); + + let invalida_data = hex::decode("00").unwrap(); + assert!(!bls_verify(&pk, &sig, &invalida_data)); + + let wrong_pk: BLSPubkey = hex::decode("98876a81fe982573ec5f986956bf9bf0bcb5349d95c3c8da0aefd05a49fea6215f59b0696f906547baed90ab245804e8").unwrap().try_into().unwrap(); + let wrong_pk = G1Affine::from_compressed(&wrong_pk).into_option().unwrap(); + assert!(!bls_verify(&wrong_pk, &sig, &data)); + + let bad_sig: BLSSignature = hex::decode("999e7b24bee2587d687e8f358ed10627ef57ec54935bd7a500bbbb18a57e7aa21b800f8b1f487a980d7c93918fdbd8020b66ce9a9e5788a4826e610ac937d8c2ce0ad9c0ee9a5732cf73052493e9a500cc5100a15bdbf9e5b79104db52dbf07c").unwrap().try_into().unwrap(); + let bad_sig = G2Affine::from_compressed(&bad_sig).into_option().unwrap(); + assert!(!bls_verify(&pk, &bad_sig, &data)) + } + + #[test] + fn test_evaluate_polynomial() { + let pks: Vec = [ + "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9", + "98876a81fe982573ec5f986956bf9bf0bcb5349d95c3c8da0aefd05a49fea6215f59b0696f906547baed90ab245804e8", + "ad2c4e5b631fbded449ede4dca2d040b9c7eae58d1e73b3050486c1ba22c15a92d9ff13c05c356f974447e4fca84864a"] + .iter().map(|pk| -> BLSPubkey { + hex::decode(pk).unwrap().try_into().unwrap() + }) + .map(|pk| G1Affine::from_compressed(&pk).into_option().unwrap()).collect(); + + let target = "af8e0095ecc662f65b95ce57e5bd2f8739ff93b0621a1ad53f5616538d1323ff40e6e9ddd7132298710974fe6fc0344e"; + + let id = bls_id_from_u32(1); + + let result = evaluate_polynomial(pks, id); + + assert!(hex::encode(result.to_compressed()) == target); + } + + #[test] + fn test_evaluate_polynomial_bad_base_keys() { + let pks: Vec = [ + "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9", + "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9", + "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9"] + .iter().map(|pk| -> BLSPubkey { + hex::decode(pk).unwrap().try_into().unwrap() + }) + .map(|pk| G1Affine::from_compressed(&pk).into_option().unwrap()).collect(); + + let target = "af8e0095ecc662f65b95ce57e5bd2f8739ff93b0621a1ad53f5616538d1323ff40e6e9ddd7132298710974fe6fc0344e"; + + let id = bls_id_from_u32(1); + + let result = evaluate_polynomial(pks, id); + + assert!(hex::encode(result.to_compressed()) != target); + } + + #[test] + fn test_lagrange_interpolation() { + let pks: Vec = [ + "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1", + "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc", + "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017"] + .iter().map(|pk| -> BLSPubkey { + hex::decode(pk).unwrap().try_into().unwrap() + }) + .map(|pk| G1Affine::from_compressed(&pk).into_option().unwrap()).collect(); + + let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b"; + + let ids = vec![ + bls_id_from_u32(1), + bls_id_from_u32(2), + bls_id_from_u32(3), + bls_id_from_u32(4), + bls_id_from_u32(5), + ]; + + let result = lagrange_interpolation(&pks, &ids); + + assert!(hex::encode(result.unwrap().to_compressed()) == target); + } + + #[test] + fn test_lagrange_interpolation_out_of_order() { + let pks: Vec = [ + "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017", + "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1", + "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc", + ] + .iter().map(|pk| -> BLSPubkey { + hex::decode(pk).unwrap().try_into().unwrap() + }) + .map(|pk| G1Affine::from_compressed(&pk).into_option().unwrap()).collect(); + + let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b"; + + let ids = vec![ + bls_id_from_u32(5), + bls_id_from_u32(1), + bls_id_from_u32(2), + bls_id_from_u32(3), + bls_id_from_u32(4), + ]; + + let result = lagrange_interpolation(&pks, &ids); + + assert!(hex::encode(result.unwrap().to_compressed()) == target); + } + + #[test] + fn test_lagrange_interpolation_wrong_order() { + let pks: Vec = [ + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502", + "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1", + "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc", + "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017"] + .iter().map(|pk| -> BLSPubkey { + hex::decode(pk).unwrap().try_into().unwrap() + }) + .map(|pk| G1Affine::from_compressed(&pk).into_option().unwrap()).collect(); + + let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b"; + + let ids = vec![ + bls_id_from_u32(1), + bls_id_from_u32(2), + bls_id_from_u32(3), + bls_id_from_u32(4), + bls_id_from_u32(5), + ]; + + let result = lagrange_interpolation(&pks, &ids); + + assert!(hex::encode(result.unwrap().to_compressed()) != target); + } + + #[test] + fn test_lagrange_interpolation_wrong_base_keys() { + let pks: Vec = [ + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903"] + .iter().map(|pk| -> BLSPubkey { + hex::decode(pk).unwrap().try_into().unwrap() + }) + .map(|pk| G1Affine::from_compressed(&pk).into_option().unwrap()).collect(); + + let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b"; + + let ids = vec![ + bls_id_from_u32(1), + bls_id_from_u32(2), + bls_id_from_u32(3), + bls_id_from_u32(4), + bls_id_from_u32(5), + ]; + + let result = lagrange_interpolation(&pks, &ids); + + assert!(hex::encode(result.unwrap().to_compressed()) != target); + } +} diff --git a/guests/dvt-circuits/bls_utils/src/lib.rs b/guests/dvt-circuits/bls_utils/src/lib.rs new file mode 100644 index 0000000..6ae7927 --- /dev/null +++ b/guests/dvt-circuits/bls_utils/src/lib.rs @@ -0,0 +1,6 @@ +pub mod bls; +pub mod verification; + +pub use verification::{ + verify_generations, verify_initial_commitment_hash, verify_seed_exchange_commitment, VerificationErrors +}; diff --git a/guests/dvt-circuits/bls_utils/src/verification.rs b/guests/dvt-circuits/bls_utils/src/verification.rs new file mode 100644 index 0000000..ea414c5 --- /dev/null +++ b/guests/dvt-circuits/bls_utils/src/verification.rs @@ -0,0 +1,338 @@ +use bls12_381::{G1Affine, G1Projective, G2Affine, Scalar}; + +use dvt_abi::{self}; +use sha2::{Digest, Sha256}; + +use crate::bls::{ + bls_id_from_u32, bls_verify, evaluate_polynomial, lagrange_interpolation, PublicKey, SecretKey, +}; + +#[derive(Debug)] +pub enum VerificationErrors { + SlashableError(String), + UnslashableError(String), +} + +impl std::fmt::Display for VerificationErrors { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + VerificationErrors::SlashableError(e) => write!(f, "{}", e), + VerificationErrors::UnslashableError(e) => write!(f, "{}", e), + } + } +} + +impl std::error::Error for VerificationErrors { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +pub fn compute_seed_exchange_hash( + seed_exchange: &dvt_abi::AbiSeedExchangeCommitment, +) -> dvt_abi::SHA256 { + let shared_secret = &seed_exchange.shared_secret; + let mut hasher = Sha256::new(); + + let sk = SecretKey::from_bytes(&shared_secret.secret).unwrap(); + + hasher.update(&seed_exchange.initial_commitment_hash); + hasher.update(&sk.to_bytes()); + hasher.update(&shared_secret.dst_base_hash); + hasher.update(&shared_secret.src_id); + hasher.update(&shared_secret.dst_id); + + hasher.finalize().to_vec().try_into().unwrap() +} + +pub fn get_index_in_commitments( + commitments: &dvt_abi::AbiVerificationHashes, + destination_id: &dvt_abi::SHA256, +) -> Result> { + let mut sorted = commitments.clone(); + sorted.sort(); + for i in 0..sorted.len() { + if commitments[i] == *destination_id { + return Ok(i as u32); + } + } + + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Could not find destination in commitments", + ))) +} + +pub fn verify_seed_exchange_commitment( + verification_hashes: &dvt_abi::AbiVerificationHashes, + seed_exchange: &dvt_abi::AbiSeedExchangeCommitment, + initial_commitment: &dvt_abi::AbiInitialCommitment, +) -> Result<(), Box> { + let commitment = &seed_exchange.commitment; + let shared_secret = &seed_exchange.shared_secret; + + if !bls_verify( + &G1Affine::from_compressed(&commitment.pubkey) + .into_option() + .unwrap(), + &G2Affine::from_compressed(&commitment.signature) + .into_option() + .unwrap(), + &commitment.hash, + ) { + // Return unslashable error + return Err(Box::new(VerificationErrors::UnslashableError( + String::from(format!( + "Invalid field seeds_exchange_commitment.commitment.signature {}\n", + hex::encode(commitment.signature) + )), + ))); + } + + let sk = SecretKey::from_bytes(&shared_secret.secret); + if sk.is_err() { + return Err(Box::new(VerificationErrors::SlashableError(String::from( + format!( + "Invalid field seeds_exchange_commitment.shared_secret.secret: {} \n", + sk.unwrap_err() + ), + )))); + } + + let sk = sk.unwrap(); + + let computed_commitment_hash = compute_seed_exchange_hash(seed_exchange); + + if computed_commitment_hash.to_vec() != seed_exchange.commitment.hash { + return Err(Box::new(VerificationErrors::SlashableError( + String::from(format!( + "Invalid field seeds_exchange_commitment.commitment.hash. Expected: {:?}, got hash: {:?}\n", + hex::encode(seed_exchange.commitment.hash), + hex::encode(computed_commitment_hash.to_vec()) + )), + ))); + } + + let dest_id = get_index_in_commitments( + verification_hashes, + &seed_exchange.shared_secret.dst_base_hash, + ); + + if dest_id.is_err() { + return Err(Box::new(VerificationErrors::SlashableError(String::from( + format!( + "Invalid field seeds_exchange_commitment.shared_secret.dst_id: {} \n", + dest_id.unwrap_err() + ), + )))); + } + + let unwraped = dest_id.unwrap() + 1; + let test_id = bls_id_from_u32(unwraped); + + let mut cfst: Vec = Vec::new(); + for pubkey in &initial_commitment.verification_vector.pubkeys { + cfst.push(G1Affine::from_compressed(pubkey).into_option().unwrap()); + } + + let le_bytes = seed_exchange.shared_secret.dst_id.clone(); + + let id = Scalar::from_bytes(&le_bytes).unwrap(); + + if id != test_id { + return Err(Box::new(VerificationErrors::SlashableError(String::from( + "Invalid field seeds_exchange_commitment.shared_secret.dst_id\n", + )))); + } + let eval_result = evaluate_polynomial(cfst, id); + + if !sk.to_public_key().eq(&eval_result) { + return Err(Box::new(VerificationErrors::SlashableError(String::from( + format!( + "Bad secret field : Expected secret with public key: {:?}, got public key: {:?}\n", + PublicKey::from_g1(&eval_result), + sk.to_public_key() + ), + )))); + } + + Ok(()) +} + +pub fn verify_initial_commitment_hash(commitment: &dvt_abi::AbiInitialCommitment) -> bool { + let mut hasher = Sha256::new(); + + hasher.update([commitment.settings.n]); + hasher.update([commitment.settings.k]); + hasher.update(commitment.settings.gen_id); + for pubkey in &commitment.verification_vector.pubkeys { + hasher.update(&pubkey); + } + let computed_hash = hasher.finalize().to_vec(); + computed_hash == commitment.hash +} + +fn verify_generation_sig( + generation: &dvt_abi::AbiGeneration, +) -> Result<(), Box> { + let partial_pubkey = PublicKey::from_bytes(&generation.partial_pubkey)?; + if !partial_pubkey + .verify_signature(&generation.message_cleartext, &generation.message_signature) + { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "Invalid signature {}", + hex::encode(generation.partial_pubkey) + ), + ))); + } + Ok(()) +} + +fn generate_initial_commitment( + generation: &dvt_abi::AbiGeneration, + settings: &dvt_abi::AbiGenerateSettings, +) -> dvt_abi::AbiInitialCommitment { + dvt_abi::AbiInitialCommitment { + hash: generation.base_hash, + settings: dvt_abi::AbiGenerateSettings { + n: settings.n, + k: settings.k, + gen_id: settings.gen_id, + }, + verification_vector: dvt_abi::AbiVerificationVector { + pubkeys: generation.verification_vector.clone(), + }, + } +} + +fn print_vec_g1_as_hex(v: &Vec) { + for i in 0..v.len() { + println!("{} ", hex::encode(v[i].to_compressed())); + } +} + +fn compute_agg_key_from_dvt( + verification_vectors: Vec, + ids: &Vec, +) -> Result> { + let verification_vectors: Vec> = verification_vectors + .iter() + .map(|vector| -> Vec { + vector + .pubkeys + .iter() + .map(|pk: &[u8; 48]| G1Affine::from_compressed(&pk).into_option().unwrap()) + .collect() + }) + .collect(); + + let mut all_pts = Vec::new(); + + for i in 0..verification_vectors.len() { + let mut pts = Vec::new(); + let share_id = ids[i]; + for j in 0..verification_vectors.len() { + let pt = evaluate_polynomial(verification_vectors[j].clone(), share_id); + pts.push(pt); + } + all_pts.push(pts); + } + let mut final_keys = Vec::new(); + + for i in 0..all_pts.len() { + let mut key: G1Affine = all_pts[i][0]; + for j in 1..all_pts[i].len() { + key = G1Affine::from(G1Projective::from(key) + G1Projective::from(all_pts[i][j])); + } + final_keys.push(key); + } + + let agg_key = lagrange_interpolation(&final_keys, &ids)?; + return Ok(agg_key); +} + +pub fn verify_generations( + generations: &[dvt_abi::AbiGeneration], + settings: &dvt_abi::AbiGenerateSettings, + agg_key: &dvt_abi::BLSPubkey, +) -> Result<(), Box> { + + if generations.len() != settings.n as usize { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid number of generations", + ))); + } + + let mut sorted = generations.to_vec(); + sorted.sort_by(|a, b| a.base_hash.cmp(&b.base_hash)); + + let verification_vectors = sorted + .iter() + .map(|generation| -> dvt_abi::AbiVerificationVector { + dvt_abi::AbiVerificationVector { + pubkeys: generation.verification_vector.clone(), + } + }) + .collect(); + + let ids = sorted + .iter() + .enumerate() + .map(|(i, _)| -> Scalar { bls_id_from_u32((i + 1) as u32) }) + .collect(); + + let computed_key = compute_agg_key_from_dvt(verification_vectors, &ids)?; + + let agg_key = G1Affine::from_compressed(agg_key).into_option(); + + if agg_key.is_none() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid aggregate public key", + ))); + } + + let agg_key = agg_key.unwrap(); + if computed_key != agg_key { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Computed key {} does not match aggregate public key {}", hex::encode(computed_key.to_compressed()), hex::encode(agg_key.to_compressed())), + ))); + } + + let partial_keys = sorted + .iter() + .map(|generation| -> G1Affine { + G1Affine::from_compressed(&generation.partial_pubkey) + .into_option() + .unwrap() + }) + .collect(); + + let computed_key = lagrange_interpolation(&partial_keys, &ids)?; + + if computed_key != agg_key { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Computed key {} does not match aggregate public key {}", hex::encode(computed_key.to_compressed()), hex::encode(agg_key.to_compressed())), + ))); + } + + for (_, generation) in generations.iter().enumerate() { + verify_generation_sig(generation)?; + let initial_commitment = generate_initial_commitment(generation, &settings); + let ok = verify_initial_commitment_hash(&initial_commitment); + if !ok { + return Err(Box::new(VerificationErrors::UnslashableError( + String::from(format!( + "Invalid initial commitment hash {}\n", + hex::encode(initial_commitment.hash) + )), + ))); + } + } + Ok(()) +} diff --git a/guests/dvt-circuits/default.env b/guests/dvt-circuits/default.env new file mode 100644 index 0000000..e69de29 diff --git a/guests/dvt-circuits/default_private_input.toml b/guests/dvt-circuits/default_private_input.toml new file mode 100644 index 0000000..ef62058 --- /dev/null +++ b/guests/dvt-circuits/default_private_input.toml @@ -0,0 +1,64 @@ +generate_settings = [5, 3, "89f0996b33ca953e0cf9b70a5f84cb50"] +generations = [ + [ + [ + "aa772b2a290804da566759b4162de6ffc8f0a47b70908f8db932a3e8e1dff73eb04f59f7549df8a33219a333bb19659c", + "a19e4b16cc1d5de8c6fd87549f9fa1fa7de066c5cb566f610909e1a712780bf8c291e82e98b023121f70d980096e4af5", + "84496615716d84d3c1d69fb295c968ed4563acd51d0fd0cc624ccce384af1d479f7adbf0ee5cce74b80f248ff8224dee" + ], + "0bcbd9433dfde67e0dd7c501e255a2bdc31d6968e51b1290e6ef7d264911eb91", + "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502", + "test for signing", + "8a751bd10706178f9dae64b18ccb758abd0535d9592ca06c3ac44831715c6cb4e66eff0b0248cdae23ec0f921963ae58040feb4862929549b690e2d8073f7b937e971c1a2387feb8d11fdc107390290bb254e941700932e15cc2d63165e09d52", + ], + + [ + [ + "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9", + "98876a81fe982573ec5f986956bf9bf0bcb5349d95c3c8da0aefd05a49fea6215f59b0696f906547baed90ab245804e8", + "ad2c4e5b631fbded449ede4dca2d040b9c7eae58d1e73b3050486c1ba22c15a92d9ff13c05c356f974447e4fca84864a" + ], + "13602b274436a91ffa3bd3eb16927e011d81fe9e2e9673769a0152acbafe3e33", + "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903", + "test for signing", + "8268c0494f7228cb71472a9c8d05374336ac65b7af2b4fb52292867595d69138a75cfc9a5ff6f102de68a7e173a57f8e13a8378ef9e103adab85b9413b6455d0bb104fc176b0e4859f5c7719ac1f63955fecefbe9eca69a4fa42eadef5b27df5" + ], + + [ + [ + "91283a9d9826347da8ef73ebe88a013989d2abecf0eb6368cb14e079da25fdf8e90fa2f793ad3566ac613882a93a531c", + "b059e3afa1f18c18eabb2be01a491b97b013b493e34bcf4fb288a7975e6a71a0e45d1d6b38d308a89726e99f09a7be85", + "a6d720d55aaae5b3a11758ca2dc5900ede471d508243b86d2adc715095c4a1efac82e96b6949763dfc288af555e8a9df" + ], + "1ae7975df76c5df0c301d390d82180ff536f5dbbf78942e53b4ec9e9e1497a1a", + "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1", + "test for signing", + "8b796dd8e8a35a6b97bd740388ad2da0399839f7ce83ae85e8651e23a0d58db44ccaca43e747713624344945ee7f25d803fe0919c669518e9bce2b0ba16e88f4ca05f5568b482b1a0068158468bc3ea8c9764b3dae9c7cb56002fcd5ab2ef25c" + ], + + [ + [ + "ac70a68387afb0ad4912970b72afc0e1143833091414498ecab27661677f7b2a79aebee35cfbd95731d3b77985b4dc72", + "97bcf8fe7f75a87f5b9e4852f4b76fdad62d8af3dc8895f4db74e74d2096b10976f262e2930c89fccc484bc9edf6c0f3", + "a04c57e48a2ad82468f702b1d444f37af91fe4a05034d003fd1ff422958d37c30d29760d443e9374aa98bc096abf4d2d" + ], + "54effb7411607a0c6484cb68cad8a30fd03e24c0fd561c5c8a4c5baa0509f9b3", + "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc", + "test for signing", + "999e7b24bee2587d687e8f358ed10627ef57ec54935bd7a500bbbb18a57e7aa21b800f8b1f487a980d7c93918fdbd8020b66ce9a9e5788a4826e610ac937d8c2ce0ad9c0ee9a5732cf73052493e9a500cc5100a15bdbf9e5b79104db52dbf07c" + ], + + [ + [ + "a6e87e5bbed258d82a13c53e04c1c3fb5baa6649a28a0d00e4a934b6473575721a7f1f1c3787145f43a1bf15368619ee", + "858276460f23124923c94bf968024dd415a39a0f638d67f1d5f27ce8d8de75273d145cd70177a26bd76c07b8e784c98a", + "8174cc034101357048634437473371c7f6b8c39e2a305c2f95fc95c13ed03939a5c3bf2bdbbaab5351e8aa3443949038" + ], + "dbd8b546ab29bfa87aa9d568e8315037695e8802fa0e0c9ed9009fe8a0fde402", + "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017", + "test for signing", + "b61e78c430eaea45f8b7974c411dd6d50eb8748fde211089838b1ca93ddefd2d9571548c4ee2eb03eec232997dc3818c1917af21083157f19966c998c35e4acf6c59e8e92bf0988692026580e425742a754eb69255e834dc75d9b729cd7ca83a" + ] +] + +aggregate_pubkey = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b" diff --git a/guests/dvt-circuits/default_public_input.toml b/guests/dvt-circuits/default_public_input.toml new file mode 100644 index 0000000..e69de29 diff --git a/guests/dvt-circuits/dvt_abi/Cargo.toml b/guests/dvt-circuits/dvt_abi/Cargo.toml new file mode 100644 index 0000000..8147faf --- /dev/null +++ b/guests/dvt-circuits/dvt_abi/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "dvt_abi" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +hex = "0.4.3" +validator = { version = "0.19.0", features = ["derive"] } \ No newline at end of file diff --git a/guests/dvt-circuits/dvt_abi/src/lib.rs b/guests/dvt-circuits/dvt_abi/src/lib.rs new file mode 100644 index 0000000..ca3d946 --- /dev/null +++ b/guests/dvt-circuits/dvt_abi/src/lib.rs @@ -0,0 +1,208 @@ +use serde::de::DeserializeOwned; +use serde::Deserialize; + +use validator::Validate; + +use hex::decode; +use std::fs::File; +use std::io::Read; + +use std::error::Error; + +pub const BLS_SIGNATURE_SIZE: usize = 96; +pub const BLS_PUBKEY_SIZE: usize = 48; +pub const BLS_SECRET_SIZE: usize = 32; +pub const BLS_ID_SIZE: usize = 32; +pub const GEN_ID_SIZE: usize = 16; +pub const SHA256_SIZE: usize = 32; + +pub type BLSPubkey = [u8; BLS_PUBKEY_SIZE]; +pub type BLSSecret = [u8; BLS_SECRET_SIZE]; +pub type BLSId = [u8; BLS_ID_SIZE]; +pub type BLSSignature = [u8; BLS_SIGNATURE_SIZE]; +pub type SHA256 = [u8; SHA256_SIZE]; + +macro_rules! decode { + ($str:ident) => { hex::decode($str).unwrap().try_into().unwrap() }; +} + +#[derive(Debug, Deserialize)] +pub struct DvtVerificationVector { + #[serde(rename(deserialize = "base_pubkeys"))] + pub pubkeys: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct DvtGenerateSettings { + pub n: u8, + pub k: u8, + pub gen_id: String, +} + +pub type DvtVerificationHashes = Vec; + +#[derive(Debug, Deserialize, Validate)] +pub struct DvtInitialCommitment { + pub hash: String, + pub settings: DvtGenerateSettings, + #[serde(rename(deserialize = "vvector"))] + pub verification_vector: DvtVerificationVector, +} + +#[derive(Debug, Deserialize)] +pub struct DvtCommitment { + pub hash: String, + pub pubkey: String, + pub signature: String, +} + +#[derive(Debug, Deserialize)] +pub struct DvtShareExchangeCommitment { + pub initial_commitment_hash: String, + #[serde(rename(deserialize = "ssecret"))] + pub shared_secret: DvtExchangedSecret, + pub commitment: DvtCommitment, +} + +#[derive(Debug, Deserialize)] +pub struct DvtExchangedSecret { + #[serde(rename(deserialize = "dst_share_id"))] + pub dst_id: String, + #[serde(rename(deserialize = "src_share_id"))] + pub src_id: String, + #[serde(rename(deserialize = "shared_secret"))] + pub secret: String, + #[serde(rename(deserialize = "dst_base_hash"))] + pub dst_base_hash: String, +} + +#[derive(Debug, Deserialize)] +pub struct DvtShare { + pub id: String, + pub pubkey: String, +} + +#[derive(Debug, Deserialize)] +pub struct DvtBlsSharedData { + #[serde(rename(deserialize = "base_hashes"))] + verification_hashes: DvtVerificationHashes, + initial_commitment: DvtInitialCommitment, + seeds_exchange_commitment: DvtShareExchangeCommitment, +} + +#[derive(Debug, Deserialize)] +pub struct DvtGeneration { + #[serde(rename(deserialize = "base_pubkeys"))] + verification_vector: Vec, + base_hash: String, + partial_pubkey: String, + message_cleartext: String, + message_signature: String, +} + +#[derive(Debug, Deserialize)] +pub struct DvtFinalizationData { + settings: DvtGenerateSettings, + generations: Vec, + aggregate_pubkey: String, +} + +#[derive(Debug)] +pub struct AbiVerificationVector { + pub pubkeys: Vec, +} + +#[derive(Debug)] +pub struct AbiGenerateSettings { + pub n: u8, + pub k: u8, + pub gen_id: [u8; GEN_ID_SIZE], +} + +impl AbiGenerateSettings { + fn new((n, k, gen_id): (u8, u8, String)) -> AbiGenerateSettings { + AbiGenerateSettings { n, k, gen_id: decode!(gen_id) } + } +} + +pub type AbiVerificationHashes = Vec; + +#[derive(Debug)] +pub struct AbiInitialCommitment { + pub hash: SHA256, + pub settings: AbiGenerateSettings, + pub verification_vector: AbiVerificationVector, +} + +#[derive(Debug)] +pub struct AbiExchangedSecret { + pub src_id: BLSId, + pub dst_id: BLSId, + pub dst_base_hash: SHA256, + pub secret: BLSSecret, +} + +#[derive(Debug)] +pub struct AbiCommitment { + pub hash: SHA256, + pub pubkey: BLSPubkey, + pub signature: BLSSignature, +} + +#[derive(Debug)] +pub struct AbiSeedExchangeCommitment { + pub initial_commitment_hash: SHA256, + pub shared_secret: AbiExchangedSecret, + pub commitment: AbiCommitment, +} + +#[derive(Debug)] +pub struct AbiBlsSharedData { + pub verification_hashes: AbiVerificationHashes, + pub initial_commitment: AbiInitialCommitment, + pub seeds_exchange_commitment: AbiSeedExchangeCommitment, +} + +#[derive(Debug, Clone)] +pub struct AbiGeneration { + pub verification_vector: Vec, + pub base_hash: SHA256, + pub partial_pubkey: BLSPubkey, + pub message_cleartext: Vec, + pub message_signature: BLSSignature, +} + +impl AbiGeneration { + fn new( + (vector, base_hash, partial_pubkey, cleartext, signature): (Vec, String, String, String, String), + ) -> AbiGeneration { + AbiGeneration { + verification_vector: vector.into_iter().map(|x| decode!(x)).collect(), + base_hash: decode!(base_hash), + partial_pubkey: decode!(partial_pubkey), + message_cleartext: cleartext.into(), + message_signature: decode!(signature), + } + } +} + +#[derive(Debug)] +pub struct AbiFinalizationData { + pub settings: AbiGenerateSettings, + pub generations: Vec, + pub aggregate_pubkey: BLSPubkey, +} + +impl AbiFinalizationData { + pub fn new( + generate_settings: (u8, u8, String), + generations: Vec<(Vec, String, String, String, String)>, + aggregate_pubkey: String, + ) -> AbiFinalizationData { + AbiFinalizationData { + settings: AbiGenerateSettings::new(generate_settings), + generations: generations.into_iter().map(|x| AbiGeneration::new(x)).collect(), + aggregate_pubkey: decode!(aggregate_pubkey), + } + } +} diff --git a/guests/dvt-circuits/src/lib.rs b/guests/dvt-circuits/src/lib.rs new file mode 100644 index 0000000..68e3a6d --- /dev/null +++ b/guests/dvt-circuits/src/lib.rs @@ -0,0 +1,18 @@ +#![no_main] + +use bls_utils; +use dvt_abi::AbiFinalizationData; + +#[guests_macro::proving_entrypoint] +pub fn main( + generate_settings: (u8, u8, String), + generations: Vec<(Vec, String, String, String, String)>, + aggregate_pubkey: String, +) { + let data = AbiFinalizationData::new(generate_settings, generations, aggregate_pubkey); + let ok = + bls_utils::verify_generations(&data.generations, &data.settings, &data.aggregate_pubkey); + if ok.is_err() { + panic!("{:?}", ok.unwrap_err().to_string()); + } +} -- cgit v1.2.3