├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── benches └── franchise.rs └── src ├── circuit ├── gadget │ ├── mod.rs │ ├── poseidon.rs │ ├── poseidon │ │ └── pow5t3.rs │ ├── utilities.rs │ └── utilities │ │ └── cond_swap.rs └── mod.rs ├── franchise.rs ├── lib.rs ├── primitives ├── mod.rs ├── poseidon.rs └── poseidon │ ├── fp.rs │ ├── fq.rs │ ├── grain.rs │ ├── mds.rs │ ├── p128pow5t3.rs │ └── test_vectors.rs ├── rustfmt.toml └── utils.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_ubuntu: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | env: 11 | RUST_BACKTRACE: 1 12 | steps: 13 | - uses: actions/checkout@v1 14 | 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: stable 18 | default: true 19 | components: rustfmt, clippy 20 | 21 | - name: Formatter 22 | uses: actions-rs/cargo@v1 23 | with: 24 | command: fmt 25 | args: --all -- --check 26 | 27 | - name: Clippy 28 | uses: actions-rs/clippy-check@v1 29 | with: 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | args: --all --all-features 32 | 33 | - name: Test 34 | uses: actions-rs/cargo@v1 35 | with: 36 | command: test 37 | args: --all --verbose 38 | 39 | # This is used to ensure that Cargo.lock is up to date 40 | - name: Check for unstaged files 41 | run: git diff --exit-code 42 | 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler32" 7 | version = "1.2.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "anyhow" 22 | version = "1.0.45" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" 25 | 26 | [[package]] 27 | name = "arrayref" 28 | version = "0.3.6" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 31 | 32 | [[package]] 33 | name = "arrayvec" 34 | version = "0.5.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 37 | 38 | [[package]] 39 | name = "atty" 40 | version = "0.2.14" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 43 | dependencies = [ 44 | "hermit-abi", 45 | "libc", 46 | "winapi", 47 | ] 48 | 49 | [[package]] 50 | name = "autocfg" 51 | version = "1.0.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 54 | 55 | [[package]] 56 | name = "bigint" 57 | version = "4.4.3" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" 60 | dependencies = [ 61 | "byteorder", 62 | "crunchy", 63 | ] 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "1.3.2" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 70 | 71 | [[package]] 72 | name = "bitvec" 73 | version = "0.22.3" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527" 76 | dependencies = [ 77 | "funty", 78 | "radium", 79 | "tap", 80 | "wyz", 81 | ] 82 | 83 | [[package]] 84 | name = "blake2b_simd" 85 | version = "0.5.11" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" 88 | dependencies = [ 89 | "arrayref", 90 | "arrayvec", 91 | "constant_time_eq", 92 | ] 93 | 94 | [[package]] 95 | name = "bstr" 96 | version = "0.2.17" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 99 | dependencies = [ 100 | "lazy_static", 101 | "memchr", 102 | "regex-automata", 103 | "serde", 104 | ] 105 | 106 | [[package]] 107 | name = "bumpalo" 108 | version = "3.8.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" 111 | 112 | [[package]] 113 | name = "bytemuck" 114 | version = "1.7.2" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" 117 | 118 | [[package]] 119 | name = "byteorder" 120 | version = "1.4.3" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 123 | 124 | [[package]] 125 | name = "cast" 126 | version = "0.2.7" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" 129 | dependencies = [ 130 | "rustc_version 0.4.0", 131 | ] 132 | 133 | [[package]] 134 | name = "cc" 135 | version = "1.0.71" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" 138 | 139 | [[package]] 140 | name = "cfg-if" 141 | version = "1.0.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 144 | 145 | [[package]] 146 | name = "chrono" 147 | version = "0.4.19" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 150 | dependencies = [ 151 | "libc", 152 | "num-integer", 153 | "num-traits", 154 | "time", 155 | "winapi", 156 | ] 157 | 158 | [[package]] 159 | name = "clap" 160 | version = "2.33.3" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 163 | dependencies = [ 164 | "bitflags", 165 | "textwrap", 166 | "unicode-width", 167 | ] 168 | 169 | [[package]] 170 | name = "cmake" 171 | version = "0.1.46" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "b7b858541263efe664aead4a5209a4ae5c5d2811167d4ed4ee0944503f8d2089" 174 | dependencies = [ 175 | "cc", 176 | ] 177 | 178 | [[package]] 179 | name = "color_quant" 180 | version = "1.1.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 183 | 184 | [[package]] 185 | name = "constant_time_eq" 186 | version = "0.1.5" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 189 | 190 | [[package]] 191 | name = "core-foundation" 192 | version = "0.9.2" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" 195 | dependencies = [ 196 | "core-foundation-sys", 197 | "libc", 198 | ] 199 | 200 | [[package]] 201 | name = "core-foundation-sys" 202 | version = "0.8.3" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 205 | 206 | [[package]] 207 | name = "core-graphics" 208 | version = "0.22.2" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" 211 | dependencies = [ 212 | "bitflags", 213 | "core-foundation", 214 | "core-graphics-types", 215 | "foreign-types", 216 | "libc", 217 | ] 218 | 219 | [[package]] 220 | name = "core-graphics-types" 221 | version = "0.1.1" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 224 | dependencies = [ 225 | "bitflags", 226 | "core-foundation", 227 | "foreign-types", 228 | "libc", 229 | ] 230 | 231 | [[package]] 232 | name = "core-text" 233 | version = "19.2.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" 236 | dependencies = [ 237 | "core-foundation", 238 | "core-graphics", 239 | "foreign-types", 240 | "libc", 241 | ] 242 | 243 | [[package]] 244 | name = "crc32fast" 245 | version = "1.2.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 248 | dependencies = [ 249 | "cfg-if", 250 | ] 251 | 252 | [[package]] 253 | name = "criterion" 254 | version = "0.3.5" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" 257 | dependencies = [ 258 | "atty", 259 | "cast", 260 | "clap", 261 | "criterion-plot", 262 | "csv", 263 | "itertools", 264 | "lazy_static", 265 | "num-traits", 266 | "oorandom", 267 | "plotters", 268 | "rayon", 269 | "regex", 270 | "serde", 271 | "serde_cbor", 272 | "serde_derive", 273 | "serde_json", 274 | "tinytemplate", 275 | "walkdir", 276 | ] 277 | 278 | [[package]] 279 | name = "criterion-plot" 280 | version = "0.4.4" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" 283 | dependencies = [ 284 | "cast", 285 | "itertools", 286 | ] 287 | 288 | [[package]] 289 | name = "crossbeam-channel" 290 | version = "0.5.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 293 | dependencies = [ 294 | "cfg-if", 295 | "crossbeam-utils", 296 | ] 297 | 298 | [[package]] 299 | name = "crossbeam-deque" 300 | version = "0.8.1" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 303 | dependencies = [ 304 | "cfg-if", 305 | "crossbeam-epoch", 306 | "crossbeam-utils", 307 | ] 308 | 309 | [[package]] 310 | name = "crossbeam-epoch" 311 | version = "0.9.5" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" 314 | dependencies = [ 315 | "cfg-if", 316 | "crossbeam-utils", 317 | "lazy_static", 318 | "memoffset", 319 | "scopeguard", 320 | ] 321 | 322 | [[package]] 323 | name = "crossbeam-utils" 324 | version = "0.8.5" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 327 | dependencies = [ 328 | "cfg-if", 329 | "lazy_static", 330 | ] 331 | 332 | [[package]] 333 | name = "crunchy" 334 | version = "0.1.6" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" 337 | 338 | [[package]] 339 | name = "csv" 340 | version = "1.1.6" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 343 | dependencies = [ 344 | "bstr", 345 | "csv-core", 346 | "itoa", 347 | "ryu", 348 | "serde", 349 | ] 350 | 351 | [[package]] 352 | name = "csv-core" 353 | version = "0.1.10" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 356 | dependencies = [ 357 | "memchr", 358 | ] 359 | 360 | [[package]] 361 | name = "darling" 362 | version = "0.10.2" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 365 | dependencies = [ 366 | "darling_core", 367 | "darling_macro", 368 | ] 369 | 370 | [[package]] 371 | name = "darling_core" 372 | version = "0.10.2" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 375 | dependencies = [ 376 | "fnv", 377 | "ident_case", 378 | "proc-macro2", 379 | "quote", 380 | "strsim", 381 | "syn", 382 | ] 383 | 384 | [[package]] 385 | name = "darling_macro" 386 | version = "0.10.2" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 389 | dependencies = [ 390 | "darling_core", 391 | "quote", 392 | "syn", 393 | ] 394 | 395 | [[package]] 396 | name = "deflate" 397 | version = "0.8.6" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" 400 | dependencies = [ 401 | "adler32", 402 | "byteorder", 403 | ] 404 | 405 | [[package]] 406 | name = "derive_builder" 407 | version = "0.9.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" 410 | dependencies = [ 411 | "darling", 412 | "derive_builder_core", 413 | "proc-macro2", 414 | "quote", 415 | "syn", 416 | ] 417 | 418 | [[package]] 419 | name = "derive_builder_core" 420 | version = "0.9.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" 423 | dependencies = [ 424 | "darling", 425 | "proc-macro2", 426 | "quote", 427 | "syn", 428 | ] 429 | 430 | [[package]] 431 | name = "dirs-next" 432 | version = "2.0.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 435 | dependencies = [ 436 | "cfg-if", 437 | "dirs-sys-next", 438 | ] 439 | 440 | [[package]] 441 | name = "dirs-sys-next" 442 | version = "0.1.2" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 445 | dependencies = [ 446 | "libc", 447 | "redox_users", 448 | "winapi", 449 | ] 450 | 451 | [[package]] 452 | name = "dwrote" 453 | version = "0.11.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" 456 | dependencies = [ 457 | "lazy_static", 458 | "libc", 459 | "winapi", 460 | "wio", 461 | ] 462 | 463 | [[package]] 464 | name = "either" 465 | version = "1.6.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 468 | 469 | [[package]] 470 | name = "expat-sys" 471 | version = "2.1.6" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" 474 | dependencies = [ 475 | "cmake", 476 | "pkg-config", 477 | ] 478 | 479 | [[package]] 480 | name = "ff" 481 | version = "0.11.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" 484 | dependencies = [ 485 | "bitvec", 486 | "rand_core", 487 | "subtle", 488 | ] 489 | 490 | [[package]] 491 | name = "float-ord" 492 | version = "0.2.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" 495 | 496 | [[package]] 497 | name = "fnv" 498 | version = "1.0.7" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 501 | 502 | [[package]] 503 | name = "font-kit" 504 | version = "0.10.1" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "46c9a156ec38864999bc9c4156e5f3b50224d4a5578028a64e5a3875caa9ee28" 507 | dependencies = [ 508 | "bitflags", 509 | "byteorder", 510 | "core-foundation", 511 | "core-graphics", 512 | "core-text", 513 | "dirs-next", 514 | "dwrote", 515 | "float-ord", 516 | "freetype", 517 | "lazy_static", 518 | "libc", 519 | "log", 520 | "pathfinder_geometry", 521 | "pathfinder_simd", 522 | "servo-fontconfig", 523 | "walkdir", 524 | "winapi", 525 | ] 526 | 527 | [[package]] 528 | name = "foreign-types" 529 | version = "0.3.2" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 532 | dependencies = [ 533 | "foreign-types-shared", 534 | ] 535 | 536 | [[package]] 537 | name = "foreign-types-shared" 538 | version = "0.1.1" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 541 | 542 | [[package]] 543 | name = "freetype" 544 | version = "0.7.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" 547 | dependencies = [ 548 | "freetype-sys", 549 | "libc", 550 | ] 551 | 552 | [[package]] 553 | name = "freetype-sys" 554 | version = "0.13.1" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" 557 | dependencies = [ 558 | "cmake", 559 | "libc", 560 | "pkg-config", 561 | ] 562 | 563 | [[package]] 564 | name = "funty" 565 | version = "1.2.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" 568 | 569 | [[package]] 570 | name = "getrandom" 571 | version = "0.2.3" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 574 | dependencies = [ 575 | "cfg-if", 576 | "libc", 577 | "wasi", 578 | ] 579 | 580 | [[package]] 581 | name = "gif" 582 | version = "0.11.3" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" 585 | dependencies = [ 586 | "color_quant", 587 | "weezl", 588 | ] 589 | 590 | [[package]] 591 | name = "group" 592 | version = "0.11.0" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" 595 | dependencies = [ 596 | "byteorder", 597 | "ff", 598 | "rand_core", 599 | "subtle", 600 | ] 601 | 602 | [[package]] 603 | name = "half" 604 | version = "1.8.2" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 607 | 608 | [[package]] 609 | name = "halo2" 610 | version = "0.1.0-beta.1" 611 | source = "git+https://github.com/adria0/halo2?branch=norayon#e39100bb73d8122dd16fad191f4923f8e43f9b5a" 612 | dependencies = [ 613 | "blake2b_simd", 614 | "ff", 615 | "group", 616 | "pasta_curves", 617 | "plotters", 618 | "rand", 619 | "rayon", 620 | "tabbycat", 621 | ] 622 | 623 | [[package]] 624 | name = "halo2" 625 | version = "0.1.0-beta.1" 626 | source = "git+https://github.com/zcash/halo2?rev=f358f7289999b202950406cdeac2ba66cd266132#f358f7289999b202950406cdeac2ba66cd266132" 627 | dependencies = [ 628 | "blake2b_simd", 629 | "ff", 630 | "group", 631 | "pasta_curves", 632 | "plotters", 633 | "rand", 634 | "rayon", 635 | "tabbycat", 636 | ] 637 | 638 | [[package]] 639 | name = "halo2-franchise" 640 | version = "0.1.0" 641 | dependencies = [ 642 | "bigint", 643 | "bitvec", 644 | "criterion", 645 | "ff", 646 | "halo2 0.1.0-beta.1 (git+https://github.com/adria0/halo2?branch=norayon)", 647 | "halo2 0.1.0-beta.1 (git+https://github.com/zcash/halo2?rev=f358f7289999b202950406cdeac2ba66cd266132)", 648 | "pasta_curves", 649 | "plotters", 650 | ] 651 | 652 | [[package]] 653 | name = "hermit-abi" 654 | version = "0.1.19" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 657 | dependencies = [ 658 | "libc", 659 | ] 660 | 661 | [[package]] 662 | name = "ident_case" 663 | version = "1.0.1" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 666 | 667 | [[package]] 668 | name = "image" 669 | version = "0.23.14" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" 672 | dependencies = [ 673 | "bytemuck", 674 | "byteorder", 675 | "color_quant", 676 | "jpeg-decoder", 677 | "num-iter", 678 | "num-rational", 679 | "num-traits", 680 | "png", 681 | ] 682 | 683 | [[package]] 684 | name = "itertools" 685 | version = "0.10.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" 688 | dependencies = [ 689 | "either", 690 | ] 691 | 692 | [[package]] 693 | name = "itoa" 694 | version = "0.4.8" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 697 | 698 | [[package]] 699 | name = "jpeg-decoder" 700 | version = "0.1.22" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" 703 | 704 | [[package]] 705 | name = "js-sys" 706 | version = "0.3.55" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" 709 | dependencies = [ 710 | "wasm-bindgen", 711 | ] 712 | 713 | [[package]] 714 | name = "lazy_static" 715 | version = "1.4.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 718 | 719 | [[package]] 720 | name = "libc" 721 | version = "0.2.103" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" 724 | 725 | [[package]] 726 | name = "log" 727 | version = "0.4.14" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 730 | dependencies = [ 731 | "cfg-if", 732 | ] 733 | 734 | [[package]] 735 | name = "memchr" 736 | version = "2.4.1" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 739 | 740 | [[package]] 741 | name = "memoffset" 742 | version = "0.6.4" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 745 | dependencies = [ 746 | "autocfg", 747 | ] 748 | 749 | [[package]] 750 | name = "miniz_oxide" 751 | version = "0.3.7" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" 754 | dependencies = [ 755 | "adler32", 756 | ] 757 | 758 | [[package]] 759 | name = "num-integer" 760 | version = "0.1.44" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 763 | dependencies = [ 764 | "autocfg", 765 | "num-traits", 766 | ] 767 | 768 | [[package]] 769 | name = "num-iter" 770 | version = "0.1.42" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" 773 | dependencies = [ 774 | "autocfg", 775 | "num-integer", 776 | "num-traits", 777 | ] 778 | 779 | [[package]] 780 | name = "num-rational" 781 | version = "0.3.2" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" 784 | dependencies = [ 785 | "autocfg", 786 | "num-integer", 787 | "num-traits", 788 | ] 789 | 790 | [[package]] 791 | name = "num-traits" 792 | version = "0.2.14" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 795 | dependencies = [ 796 | "autocfg", 797 | ] 798 | 799 | [[package]] 800 | name = "num_cpus" 801 | version = "1.13.0" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 804 | dependencies = [ 805 | "hermit-abi", 806 | "libc", 807 | ] 808 | 809 | [[package]] 810 | name = "oorandom" 811 | version = "11.1.3" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 814 | 815 | [[package]] 816 | name = "pasta_curves" 817 | version = "0.2.1" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "d647d91972bad78120fd61e06b225fcda117805c9bbf17676b51bd03a251278b" 820 | dependencies = [ 821 | "blake2b_simd", 822 | "ff", 823 | "group", 824 | "lazy_static", 825 | "rand", 826 | "static_assertions", 827 | "subtle", 828 | ] 829 | 830 | [[package]] 831 | name = "pathfinder_geometry" 832 | version = "0.5.1" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" 835 | dependencies = [ 836 | "log", 837 | "pathfinder_simd", 838 | ] 839 | 840 | [[package]] 841 | name = "pathfinder_simd" 842 | version = "0.5.1" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" 845 | dependencies = [ 846 | "rustc_version 0.3.3", 847 | ] 848 | 849 | [[package]] 850 | name = "pest" 851 | version = "2.1.3" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 854 | dependencies = [ 855 | "ucd-trie", 856 | ] 857 | 858 | [[package]] 859 | name = "pkg-config" 860 | version = "0.3.22" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" 863 | 864 | [[package]] 865 | name = "plotters" 866 | version = "0.3.1" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" 869 | dependencies = [ 870 | "chrono", 871 | "font-kit", 872 | "image", 873 | "lazy_static", 874 | "num-traits", 875 | "pathfinder_geometry", 876 | "plotters-backend", 877 | "plotters-bitmap", 878 | "plotters-svg", 879 | "ttf-parser", 880 | "wasm-bindgen", 881 | "web-sys", 882 | ] 883 | 884 | [[package]] 885 | name = "plotters-backend" 886 | version = "0.3.2" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" 889 | 890 | [[package]] 891 | name = "plotters-bitmap" 892 | version = "0.3.1" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "21362fa905695e5618aefd169358f52e0e8bc4a8e05333cf780fda8cddc00b54" 895 | dependencies = [ 896 | "gif", 897 | "image", 898 | "plotters-backend", 899 | ] 900 | 901 | [[package]] 902 | name = "plotters-svg" 903 | version = "0.3.1" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" 906 | dependencies = [ 907 | "plotters-backend", 908 | ] 909 | 910 | [[package]] 911 | name = "png" 912 | version = "0.16.8" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" 915 | dependencies = [ 916 | "bitflags", 917 | "crc32fast", 918 | "deflate", 919 | "miniz_oxide", 920 | ] 921 | 922 | [[package]] 923 | name = "ppv-lite86" 924 | version = "0.2.14" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741" 927 | 928 | [[package]] 929 | name = "proc-macro2" 930 | version = "1.0.30" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" 933 | dependencies = [ 934 | "unicode-xid", 935 | ] 936 | 937 | [[package]] 938 | name = "quote" 939 | version = "1.0.10" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 942 | dependencies = [ 943 | "proc-macro2", 944 | ] 945 | 946 | [[package]] 947 | name = "radium" 948 | version = "0.6.2" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" 951 | 952 | [[package]] 953 | name = "rand" 954 | version = "0.8.4" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 957 | dependencies = [ 958 | "libc", 959 | "rand_chacha", 960 | "rand_core", 961 | "rand_hc", 962 | ] 963 | 964 | [[package]] 965 | name = "rand_chacha" 966 | version = "0.3.1" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 969 | dependencies = [ 970 | "ppv-lite86", 971 | "rand_core", 972 | ] 973 | 974 | [[package]] 975 | name = "rand_core" 976 | version = "0.6.3" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 979 | dependencies = [ 980 | "getrandom", 981 | ] 982 | 983 | [[package]] 984 | name = "rand_hc" 985 | version = "0.3.1" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 988 | dependencies = [ 989 | "rand_core", 990 | ] 991 | 992 | [[package]] 993 | name = "rayon" 994 | version = "1.5.1" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" 997 | dependencies = [ 998 | "autocfg", 999 | "crossbeam-deque", 1000 | "either", 1001 | "rayon-core", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "rayon-core" 1006 | version = "1.9.1" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" 1009 | dependencies = [ 1010 | "crossbeam-channel", 1011 | "crossbeam-deque", 1012 | "crossbeam-utils", 1013 | "lazy_static", 1014 | "num_cpus", 1015 | ] 1016 | 1017 | [[package]] 1018 | name = "redox_syscall" 1019 | version = "0.2.10" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 1022 | dependencies = [ 1023 | "bitflags", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "redox_users" 1028 | version = "0.4.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" 1031 | dependencies = [ 1032 | "getrandom", 1033 | "redox_syscall", 1034 | ] 1035 | 1036 | [[package]] 1037 | name = "regex" 1038 | version = "1.5.4" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1041 | dependencies = [ 1042 | "aho-corasick", 1043 | "memchr", 1044 | "regex-syntax", 1045 | ] 1046 | 1047 | [[package]] 1048 | name = "regex-automata" 1049 | version = "0.1.10" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1052 | 1053 | [[package]] 1054 | name = "regex-syntax" 1055 | version = "0.6.25" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1058 | 1059 | [[package]] 1060 | name = "rustc_version" 1061 | version = "0.3.3" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" 1064 | dependencies = [ 1065 | "semver 0.11.0", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "rustc_version" 1070 | version = "0.4.0" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1073 | dependencies = [ 1074 | "semver 1.0.4", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "ryu" 1079 | version = "1.0.5" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1082 | 1083 | [[package]] 1084 | name = "same-file" 1085 | version = "1.0.6" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1088 | dependencies = [ 1089 | "winapi-util", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "scopeguard" 1094 | version = "1.1.0" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1097 | 1098 | [[package]] 1099 | name = "semver" 1100 | version = "0.11.0" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 1103 | dependencies = [ 1104 | "semver-parser", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "semver" 1109 | version = "1.0.4" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" 1112 | 1113 | [[package]] 1114 | name = "semver-parser" 1115 | version = "0.10.2" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 1118 | dependencies = [ 1119 | "pest", 1120 | ] 1121 | 1122 | [[package]] 1123 | name = "serde" 1124 | version = "1.0.130" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 1127 | 1128 | [[package]] 1129 | name = "serde_cbor" 1130 | version = "0.11.2" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" 1133 | dependencies = [ 1134 | "half", 1135 | "serde", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "serde_derive" 1140 | version = "1.0.130" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 1143 | dependencies = [ 1144 | "proc-macro2", 1145 | "quote", 1146 | "syn", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "serde_json" 1151 | version = "1.0.70" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3" 1154 | dependencies = [ 1155 | "itoa", 1156 | "ryu", 1157 | "serde", 1158 | ] 1159 | 1160 | [[package]] 1161 | name = "servo-fontconfig" 1162 | version = "0.5.1" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" 1165 | dependencies = [ 1166 | "libc", 1167 | "servo-fontconfig-sys", 1168 | ] 1169 | 1170 | [[package]] 1171 | name = "servo-fontconfig-sys" 1172 | version = "5.1.0" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" 1175 | dependencies = [ 1176 | "expat-sys", 1177 | "freetype-sys", 1178 | "pkg-config", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "static_assertions" 1183 | version = "1.1.0" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1186 | 1187 | [[package]] 1188 | name = "strsim" 1189 | version = "0.9.3" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 1192 | 1193 | [[package]] 1194 | name = "subtle" 1195 | version = "2.4.1" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1198 | 1199 | [[package]] 1200 | name = "syn" 1201 | version = "1.0.80" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" 1204 | dependencies = [ 1205 | "proc-macro2", 1206 | "quote", 1207 | "unicode-xid", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "tabbycat" 1212 | version = "0.1.2" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "c45590f0f859197b4545be1b17b2bc3cc7bb075f7d1cc0ea1dc6521c0bf256a3" 1215 | dependencies = [ 1216 | "anyhow", 1217 | "derive_builder", 1218 | "regex", 1219 | ] 1220 | 1221 | [[package]] 1222 | name = "tap" 1223 | version = "1.0.1" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 1226 | 1227 | [[package]] 1228 | name = "textwrap" 1229 | version = "0.11.0" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1232 | dependencies = [ 1233 | "unicode-width", 1234 | ] 1235 | 1236 | [[package]] 1237 | name = "time" 1238 | version = "0.1.43" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1241 | dependencies = [ 1242 | "libc", 1243 | "winapi", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "tinytemplate" 1248 | version = "1.2.1" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 1251 | dependencies = [ 1252 | "serde", 1253 | "serde_json", 1254 | ] 1255 | 1256 | [[package]] 1257 | name = "ttf-parser" 1258 | version = "0.12.3" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" 1261 | 1262 | [[package]] 1263 | name = "ucd-trie" 1264 | version = "0.1.3" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1267 | 1268 | [[package]] 1269 | name = "unicode-width" 1270 | version = "0.1.9" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1273 | 1274 | [[package]] 1275 | name = "unicode-xid" 1276 | version = "0.2.2" 1277 | source = "registry+https://github.com/rust-lang/crates.io-index" 1278 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1279 | 1280 | [[package]] 1281 | name = "walkdir" 1282 | version = "2.3.2" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 1285 | dependencies = [ 1286 | "same-file", 1287 | "winapi", 1288 | "winapi-util", 1289 | ] 1290 | 1291 | [[package]] 1292 | name = "wasi" 1293 | version = "0.10.2+wasi-snapshot-preview1" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1296 | 1297 | [[package]] 1298 | name = "wasm-bindgen" 1299 | version = "0.2.78" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" 1302 | dependencies = [ 1303 | "cfg-if", 1304 | "wasm-bindgen-macro", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "wasm-bindgen-backend" 1309 | version = "0.2.78" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" 1312 | dependencies = [ 1313 | "bumpalo", 1314 | "lazy_static", 1315 | "log", 1316 | "proc-macro2", 1317 | "quote", 1318 | "syn", 1319 | "wasm-bindgen-shared", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "wasm-bindgen-macro" 1324 | version = "0.2.78" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" 1327 | dependencies = [ 1328 | "quote", 1329 | "wasm-bindgen-macro-support", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "wasm-bindgen-macro-support" 1334 | version = "0.2.78" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" 1337 | dependencies = [ 1338 | "proc-macro2", 1339 | "quote", 1340 | "syn", 1341 | "wasm-bindgen-backend", 1342 | "wasm-bindgen-shared", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "wasm-bindgen-shared" 1347 | version = "0.2.78" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" 1350 | 1351 | [[package]] 1352 | name = "web-sys" 1353 | version = "0.3.55" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" 1356 | dependencies = [ 1357 | "js-sys", 1358 | "wasm-bindgen", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "weezl" 1363 | version = "0.1.5" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" 1366 | 1367 | [[package]] 1368 | name = "winapi" 1369 | version = "0.3.9" 1370 | source = "registry+https://github.com/rust-lang/crates.io-index" 1371 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1372 | dependencies = [ 1373 | "winapi-i686-pc-windows-gnu", 1374 | "winapi-x86_64-pc-windows-gnu", 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "winapi-i686-pc-windows-gnu" 1379 | version = "0.4.0" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1382 | 1383 | [[package]] 1384 | name = "winapi-util" 1385 | version = "0.1.5" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1388 | dependencies = [ 1389 | "winapi", 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "winapi-x86_64-pc-windows-gnu" 1394 | version = "0.4.0" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1397 | 1398 | [[package]] 1399 | name = "wio" 1400 | version = "0.2.2" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" 1403 | dependencies = [ 1404 | "winapi", 1405 | ] 1406 | 1407 | [[package]] 1408 | name = "wyz" 1409 | version = "0.4.0" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188" 1412 | dependencies = [ 1413 | "tap", 1414 | ] 1415 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2-franchise" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | halo2_zcash = { package="halo2", git = "https://github.com/zcash/halo2", rev ="f358f7289999b202950406cdeac2ba66cd266132", features=["dev-graph"], optional = true } 10 | halo2_adria0 = { package="halo2", git = "https://github.com/adria0/halo2", branch="norayon", features=["dev-graph"], optional=true } 11 | 12 | plotters = "0.3" 13 | pasta_curves = "0.2.1" 14 | ff = "0.11" 15 | 16 | bigint = "4.4" 17 | bitvec = "0.22" 18 | 19 | [dev-dependencies] 20 | criterion = "0.3" 21 | 22 | [features] 23 | default = [ "halo2_zcash" ] 24 | wasm = ["halo2_adria0"] 25 | 26 | [[bench]] 27 | name = "franchise" 28 | harness = false 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Rust build](https://github.com/vocdoni/halo2-franchise-proof/actions/workflows/rust.yml/badge.svg)](https://github.com/vocdoni/halo2-franchise-proof/actions/workflows/rust.yml) 2 | 3 | # halo2-franchise-proof 4 | 5 | This is an experimental port of the Vocdoni voting anonymizer zk circuit (see [current implementation](https://github.com/vocdoni/zk-franchise-proof-circuit) using [Groth16](http://www.zeroknowledgeblog.com/index.php/groth16)/[circom](https://github.com/iden3/circom)) to the [ZCash Halo2](https://zcash.github.io/halo2) proving system that does not have trusted setup. 6 | 7 | ## Run benchmarks 8 | 9 | run `cargo bench` 10 | 11 | current results in an M1 are: 12 | 13 | | Merkle tree levels | Prove (ms) | Verify (ms) | 14 | | -------- | -------- | -------- | 15 | | 9 | 172.49 | 6.29 | 16 | | 21 | 283.62 | 8.32 | 17 | 18 | when running in a wasm/browser context in a mobile (Galaxy A41) the execution time is about 10s in a single-threaded environment. 19 | 20 | -------------------------------------------------------------------------------- /benches/franchise.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use criterion::Criterion; 5 | use halo2_franchise::halo2::pasta::EqAffine; 6 | use halo2_franchise::halo2::plonk::*; 7 | use halo2_franchise::halo2::poly::commitment::Params; 8 | use halo2_franchise::halo2::transcript::{Blake2bRead, Blake2bWrite, Challenge255}; 9 | use halo2_franchise::{franchise::FranchiseCircuit, utils::generate_test_data}; 10 | 11 | fn bench(k: u32, c: &mut Criterion) { 12 | let params: Params = Params::new(k); 13 | let empty_circuit = FranchiseCircuit::::default(); 14 | 15 | // Initialize the proving key 16 | let vk = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail"); 17 | let pk = keygen_pk(¶ms, vk, &empty_circuit).expect("keygen_pk should not fail"); 18 | 19 | let (circuit, public) = generate_test_data::(); 20 | 21 | let prover_name = format!("franchise-prove-k{}-lvl{}", k, LVL); 22 | let verifier_name = format!("franchise-verify-k{}-lvl{}", k, LVL); 23 | 24 | c.bench_function(&prover_name, |b| { 25 | b.iter(|| { 26 | let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); 27 | create_proof( 28 | ¶ms, 29 | &pk, 30 | &[circuit.clone()], 31 | &[&[&public]], 32 | &mut transcript, 33 | ) 34 | .expect("proof generation should not fail"); 35 | transcript.finalize(); 36 | }) 37 | }); 38 | 39 | let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); 40 | create_proof(¶ms, &pk, &[circuit], &[&[&public]], &mut transcript) 41 | .expect("proof generation should not fail"); 42 | let proof = transcript.finalize(); 43 | 44 | c.bench_function(&verifier_name, |b| { 45 | b.iter(|| { 46 | let msm = params.empty_msm(); 47 | let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); 48 | let guard = 49 | verify_proof(¶ms, pk.get_vk(), msm, &[&[&public]], &mut transcript).unwrap(); 50 | let msm = guard.clone().use_challenges(); 51 | assert!(msm.eval()); 52 | }) 53 | }); 54 | } 55 | 56 | fn criterion_benchmark(c: &mut Criterion) { 57 | bench::<9>(9, c); 58 | bench::<21>(10, c); 59 | } 60 | 61 | criterion_group!(benches, criterion_benchmark); 62 | 63 | criterion_main!(benches); 64 | -------------------------------------------------------------------------------- /src/circuit/gadget/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod poseidon; 2 | pub mod utilities; 3 | -------------------------------------------------------------------------------- /src/circuit/gadget/poseidon.rs: -------------------------------------------------------------------------------- 1 | //! Gadget and chips for the Poseidon algebraic hash function. 2 | 3 | use std::array; 4 | use std::fmt; 5 | 6 | use crate::halo2::{ 7 | arithmetic::FieldExt, 8 | circuit::{Chip, Layouter}, 9 | plonk::Error, 10 | }; 11 | 12 | mod pow5t3; 13 | pub use pow5t3::{Pow5T3Chip, Pow5T3Config, StateWord}; 14 | 15 | use crate::primitives::poseidon::{ConstantLength, Domain, Spec, Sponge, SpongeState, State}; 16 | 17 | /// The set of circuit instructions required to use the Poseidon permutation. 18 | pub trait PoseidonInstructions, const T: usize, const RATE: usize>: 19 | Chip 20 | { 21 | /// Variable representing the word over which the Poseidon permutation operates. 22 | type Word: Copy + fmt::Debug; 23 | 24 | /// Applies the Poseidon permutation to the given state. 25 | fn permute( 26 | &self, 27 | layouter: &mut impl Layouter, 28 | initial_state: &State, 29 | ) -> Result, Error>; 30 | } 31 | 32 | /// The set of circuit instructions required to use the [`Duplex`] and [`Hash`] gadgets. 33 | /// 34 | /// [`Hash`]: self::Hash 35 | pub trait PoseidonDuplexInstructions< 36 | F: FieldExt, 37 | S: Spec, 38 | const T: usize, 39 | const RATE: usize, 40 | >: PoseidonInstructions 41 | { 42 | /// Returns the initial empty state for the given domain. 43 | fn initial_state( 44 | &self, 45 | layouter: &mut impl Layouter, 46 | domain: &impl Domain, 47 | ) -> Result, Error>; 48 | 49 | /// Pads the given input (according to the specified domain) and adds it to the state. 50 | fn pad_and_add( 51 | &self, 52 | layouter: &mut impl Layouter, 53 | domain: &impl Domain, 54 | initial_state: &State, 55 | input: &SpongeState, 56 | ) -> Result, Error>; 57 | 58 | /// Extracts sponge output from the given state. 59 | fn get_output(state: &State) -> SpongeState; 60 | } 61 | 62 | /// A word over which the Poseidon permutation operates. 63 | pub struct Word< 64 | F: FieldExt, 65 | PoseidonChip: PoseidonInstructions, 66 | S: Spec, 67 | const T: usize, 68 | const RATE: usize, 69 | > { 70 | inner: PoseidonChip::Word, 71 | } 72 | 73 | impl< 74 | F: FieldExt, 75 | PoseidonChip: PoseidonInstructions, 76 | S: Spec, 77 | const T: usize, 78 | const RATE: usize, 79 | > Word 80 | { 81 | pub fn inner(&self) -> PoseidonChip::Word { 82 | self.inner 83 | } 84 | 85 | pub fn from_inner(inner: PoseidonChip::Word) -> Self { 86 | Self { inner } 87 | } 88 | } 89 | 90 | fn poseidon_duplex< 91 | F: FieldExt, 92 | PoseidonChip: PoseidonDuplexInstructions, 93 | S: Spec, 94 | D: Domain, 95 | const T: usize, 96 | const RATE: usize, 97 | >( 98 | chip: &PoseidonChip, 99 | mut layouter: impl Layouter, 100 | domain: &D, 101 | state: &mut State, 102 | input: &SpongeState, 103 | ) -> Result, Error> { 104 | *state = chip.pad_and_add(&mut layouter, domain, state, input)?; 105 | *state = chip.permute(&mut layouter, state)?; 106 | Ok(PoseidonChip::get_output(state)) 107 | } 108 | 109 | /// A Poseidon duplex sponge. 110 | pub struct Duplex< 111 | F: FieldExt, 112 | PoseidonChip: PoseidonDuplexInstructions, 113 | S: Spec, 114 | D: Domain, 115 | const T: usize, 116 | const RATE: usize, 117 | > { 118 | chip: PoseidonChip, 119 | sponge: Sponge, 120 | state: State, 121 | domain: D, 122 | } 123 | 124 | impl< 125 | F: FieldExt, 126 | PoseidonChip: PoseidonDuplexInstructions, 127 | S: Spec, 128 | D: Domain, 129 | const T: usize, 130 | const RATE: usize, 131 | > Duplex 132 | { 133 | /// Constructs a new duplex sponge for the given Poseidon specification. 134 | pub fn new( 135 | chip: PoseidonChip, 136 | mut layouter: impl Layouter, 137 | domain: D, 138 | ) -> Result { 139 | chip.initial_state(&mut layouter, &domain) 140 | .map(|state| Duplex { 141 | chip, 142 | sponge: Sponge::Absorbing([None; RATE]), 143 | state, 144 | domain, 145 | }) 146 | } 147 | 148 | /// Absorbs an element into the sponge. 149 | pub fn absorb( 150 | &mut self, 151 | mut layouter: impl Layouter, 152 | value: Word, 153 | ) -> Result<(), Error> { 154 | match self.sponge { 155 | Sponge::Absorbing(ref mut input) => { 156 | for entry in input.iter_mut() { 157 | if entry.is_none() { 158 | *entry = Some(value.inner); 159 | return Ok(()); 160 | } 161 | } 162 | 163 | // We've already absorbed as many elements as we can 164 | let _ = poseidon_duplex( 165 | &self.chip, 166 | layouter.namespace(|| "PoseidonDuplex"), 167 | &self.domain, 168 | &mut self.state, 169 | input, 170 | )?; 171 | self.sponge = Sponge::absorb(value.inner); 172 | } 173 | Sponge::Squeezing(_) => { 174 | // Drop the remaining output elements 175 | self.sponge = Sponge::absorb(value.inner); 176 | } 177 | } 178 | 179 | Ok(()) 180 | } 181 | 182 | /// Squeezes an element from the sponge. 183 | pub fn squeeze( 184 | &mut self, 185 | mut layouter: impl Layouter, 186 | ) -> Result, Error> { 187 | loop { 188 | match self.sponge { 189 | Sponge::Absorbing(ref input) => { 190 | self.sponge = Sponge::Squeezing(poseidon_duplex( 191 | &self.chip, 192 | layouter.namespace(|| "PoseidonDuplex"), 193 | &self.domain, 194 | &mut self.state, 195 | input, 196 | )?); 197 | } 198 | Sponge::Squeezing(ref mut output) => { 199 | for entry in output.iter_mut() { 200 | if let Some(inner) = entry.take() { 201 | return Ok(Word { inner }); 202 | } 203 | } 204 | 205 | // We've already squeezed out all available elements 206 | self.sponge = Sponge::Absorbing([None; RATE]); 207 | } 208 | } 209 | } 210 | } 211 | } 212 | 213 | /// A Poseidon hash function, built around a duplex sponge. 214 | pub struct Hash< 215 | F: FieldExt, 216 | PoseidonChip: PoseidonDuplexInstructions, 217 | S: Spec, 218 | D: Domain, 219 | const T: usize, 220 | const RATE: usize, 221 | > { 222 | duplex: Duplex, 223 | } 224 | 225 | impl< 226 | F: FieldExt, 227 | PoseidonChip: PoseidonDuplexInstructions, 228 | S: Spec, 229 | D: Domain, 230 | const T: usize, 231 | const RATE: usize, 232 | > Hash 233 | { 234 | /// Initializes a new hasher. 235 | pub fn init(chip: PoseidonChip, layouter: impl Layouter, domain: D) -> Result { 236 | Duplex::new(chip, layouter, domain).map(|duplex| Hash { duplex }) 237 | } 238 | } 239 | 240 | impl< 241 | F: FieldExt, 242 | PoseidonChip: PoseidonDuplexInstructions, 243 | S: Spec, 244 | const T: usize, 245 | const RATE: usize, 246 | const L: usize, 247 | > Hash, T, RATE> 248 | { 249 | /// Hashes the given input. 250 | pub fn hash( 251 | mut self, 252 | mut layouter: impl Layouter, 253 | message: [Word; L], 254 | ) -> Result, Error> { 255 | for (i, value) in array::IntoIter::new(message).enumerate() { 256 | self.duplex 257 | .absorb(layouter.namespace(|| format!("absorb_{}", i)), value)?; 258 | } 259 | self.duplex.squeeze(layouter.namespace(|| "squeeze")) 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/circuit/gadget/poseidon/pow5t3.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use crate::halo2::{ 4 | arithmetic::FieldExt, 5 | circuit::{Cell, Chip, Layouter, Region}, 6 | plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector}, 7 | poly::Rotation, 8 | }; 9 | 10 | use super::{PoseidonDuplexInstructions, PoseidonInstructions}; 11 | use crate::circuit::gadget::utilities::{CellValue, Var}; 12 | use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State}; 13 | 14 | pub const WIDTH: usize = 3; 15 | 16 | /// Configuration for an [`Pow5T3Chip`]. 17 | #[derive(Clone, Debug)] 18 | pub struct Pow5T3Config { 19 | pub(in crate::circuit) state: [Column; WIDTH], 20 | partial_sbox: Column, 21 | rc_a: [Column; WIDTH], 22 | rc_b: [Column; WIDTH], 23 | s_full: Selector, 24 | s_partial: Selector, 25 | s_pad_and_add: Selector, 26 | 27 | half_full_rounds: usize, 28 | half_partial_rounds: usize, 29 | alpha: [u64; 4], 30 | round_constants: Vec<[F; WIDTH]>, 31 | m_reg: Mds, 32 | m_inv: Mds, 33 | } 34 | 35 | /// A Poseidon chip using an $x^5$ S-Box, with a width of 3, suitable for a 2:1 reduction. 36 | #[derive(Debug)] 37 | pub struct Pow5T3Chip { 38 | config: Pow5T3Config, 39 | } 40 | 41 | impl Pow5T3Chip { 42 | /// Configures this chip for use in a circuit. 43 | /// 44 | /// # Side-effects 45 | /// 46 | /// All columns in `state` will be equality-enabled. 47 | // 48 | // TODO: Does the rate need to be hard-coded here, or only the width? It probably 49 | // needs to be known wherever we implement the hashing gadget, but it isn't strictly 50 | // necessary for the permutation. 51 | pub fn configure>( 52 | meta: &mut ConstraintSystem, 53 | spec: S, 54 | state: [Column; WIDTH], 55 | partial_sbox: Column, 56 | rc_a: [Column; WIDTH], 57 | rc_b: [Column; WIDTH], 58 | ) -> Pow5T3Config { 59 | // Generate constants for the Poseidon permutation. 60 | // This gadget requires R_F and R_P to be even. 61 | assert!(S::full_rounds() & 1 == 0); 62 | assert!(S::partial_rounds() & 1 == 0); 63 | let half_full_rounds = S::full_rounds() / 2; 64 | let half_partial_rounds = S::partial_rounds() / 2; 65 | let (round_constants, m_reg, m_inv) = spec.constants(); 66 | 67 | // This allows state words to be initialized (by constraining them equal to fixed 68 | // values), and used in a permutation from an arbitrary region. rc_a is used in 69 | // every permutation round, while rc_b is empty in the initial and final full 70 | // rounds, so we use rc_b as "scratch space" for fixed values (enabling potential 71 | // layouter optimisations). 72 | for column in iter::empty() 73 | .chain(state.iter().cloned().map(|c| c.into())) 74 | .chain(rc_b.iter().cloned().map(|c| c.into())) 75 | { 76 | meta.enable_equality(column); 77 | } 78 | 79 | let s_full = meta.selector(); 80 | let s_partial = meta.selector(); 81 | let s_pad_and_add = meta.selector(); 82 | 83 | let alpha = [5, 0, 0, 0]; 84 | let pow_5 = |v: Expression| { 85 | let v2 = v.clone() * v.clone(); 86 | v2.clone() * v2 * v 87 | }; 88 | 89 | meta.create_gate("full round", |meta| { 90 | let cur_0 = meta.query_advice(state[0], Rotation::cur()); 91 | let cur_1 = meta.query_advice(state[1], Rotation::cur()); 92 | let cur_2 = meta.query_advice(state[2], Rotation::cur()); 93 | let next = [ 94 | meta.query_advice(state[0], Rotation::next()), 95 | meta.query_advice(state[1], Rotation::next()), 96 | meta.query_advice(state[2], Rotation::next()), 97 | ]; 98 | 99 | let rc_0 = meta.query_fixed(rc_a[0], Rotation::cur()); 100 | let rc_1 = meta.query_fixed(rc_a[1], Rotation::cur()); 101 | let rc_2 = meta.query_fixed(rc_a[2], Rotation::cur()); 102 | 103 | let s_full = meta.query_selector(s_full); 104 | 105 | let full_round = |next_idx: usize| { 106 | s_full.clone() 107 | * (pow_5(cur_0.clone() + rc_0.clone()) * m_reg[next_idx][0] 108 | + pow_5(cur_1.clone() + rc_1.clone()) * m_reg[next_idx][1] 109 | + pow_5(cur_2.clone() + rc_2.clone()) * m_reg[next_idx][2] 110 | - next[next_idx].clone()) 111 | }; 112 | 113 | vec![ 114 | ("state[0]", full_round(0)), 115 | ("state[1]", full_round(1)), 116 | ("state[2]", full_round(2)), 117 | ] 118 | }); 119 | 120 | meta.create_gate("partial rounds", |meta| { 121 | let cur_0 = meta.query_advice(state[0], Rotation::cur()); 122 | let cur_1 = meta.query_advice(state[1], Rotation::cur()); 123 | let cur_2 = meta.query_advice(state[2], Rotation::cur()); 124 | let mid_0 = meta.query_advice(partial_sbox, Rotation::cur()); 125 | let next_0 = meta.query_advice(state[0], Rotation::next()); 126 | let next_1 = meta.query_advice(state[1], Rotation::next()); 127 | let next_2 = meta.query_advice(state[2], Rotation::next()); 128 | 129 | let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur()); 130 | let rc_a1 = meta.query_fixed(rc_a[1], Rotation::cur()); 131 | let rc_a2 = meta.query_fixed(rc_a[2], Rotation::cur()); 132 | let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur()); 133 | let rc_b1 = meta.query_fixed(rc_b[1], Rotation::cur()); 134 | let rc_b2 = meta.query_fixed(rc_b[2], Rotation::cur()); 135 | 136 | let s_partial = meta.query_selector(s_partial); 137 | 138 | let partial_round_linear = |idx: usize, rc_b: Expression| { 139 | s_partial.clone() 140 | * (mid_0.clone() * m_reg[idx][0] 141 | + (cur_1.clone() + rc_a1.clone()) * m_reg[idx][1] 142 | + (cur_2.clone() + rc_a2.clone()) * m_reg[idx][2] 143 | + rc_b 144 | - (next_0.clone() * m_inv[idx][0] 145 | + next_1.clone() * m_inv[idx][1] 146 | + next_2.clone() * m_inv[idx][2])) 147 | }; 148 | 149 | vec![ 150 | ( 151 | "state[0] round a", 152 | s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()), 153 | ), 154 | ( 155 | "state[0] round b", 156 | s_partial.clone() 157 | * (pow_5( 158 | mid_0.clone() * m_reg[0][0] 159 | + (cur_1.clone() + rc_a1.clone()) * m_reg[0][1] 160 | + (cur_2.clone() + rc_a2.clone()) * m_reg[0][2] 161 | + rc_b0, 162 | ) - (next_0.clone() * m_inv[0][0] 163 | + next_1.clone() * m_inv[0][1] 164 | + next_2.clone() * m_inv[0][2])), 165 | ), 166 | ("state[1]", partial_round_linear(1, rc_b1)), 167 | ("state[2]", partial_round_linear(2, rc_b2)), 168 | ] 169 | }); 170 | 171 | meta.create_gate("pad-and-add", |meta| { 172 | let initial_state_0 = meta.query_advice(state[0], Rotation::prev()); 173 | let initial_state_1 = meta.query_advice(state[1], Rotation::prev()); 174 | let initial_state_2 = meta.query_advice(state[2], Rotation::prev()); 175 | let input_0 = meta.query_advice(state[0], Rotation::cur()); 176 | let input_1 = meta.query_advice(state[1], Rotation::cur()); 177 | let output_state_0 = meta.query_advice(state[0], Rotation::next()); 178 | let output_state_1 = meta.query_advice(state[1], Rotation::next()); 179 | let output_state_2 = meta.query_advice(state[2], Rotation::next()); 180 | 181 | let s_pad_and_add = meta.query_selector(s_pad_and_add); 182 | 183 | let pad_and_add = |initial_state, input, output_state| { 184 | // We pad the input by storing the required padding in fixed columns and 185 | // then constraining the corresponding input columns to be equal to it. 186 | s_pad_and_add.clone() * (initial_state + input - output_state) 187 | }; 188 | 189 | vec![ 190 | ( 191 | "state[0]", 192 | pad_and_add(initial_state_0, input_0, output_state_0), 193 | ), 194 | ( 195 | "state[1]", 196 | pad_and_add(initial_state_1, input_1, output_state_1), 197 | ), 198 | // The capacity element is never altered by the input. 199 | ( 200 | "state[2]", 201 | s_pad_and_add * (initial_state_2 - output_state_2), 202 | ), 203 | ] 204 | }); 205 | 206 | Pow5T3Config { 207 | state, 208 | partial_sbox, 209 | rc_a, 210 | rc_b, 211 | s_full, 212 | s_partial, 213 | s_pad_and_add, 214 | half_full_rounds, 215 | half_partial_rounds, 216 | alpha, 217 | round_constants, 218 | m_reg, 219 | m_inv, 220 | } 221 | } 222 | 223 | pub fn construct(config: Pow5T3Config) -> Self { 224 | Pow5T3Chip { config } 225 | } 226 | } 227 | 228 | impl Chip for Pow5T3Chip { 229 | type Config = Pow5T3Config; 230 | type Loaded = (); 231 | 232 | fn config(&self) -> &Self::Config { 233 | &self.config 234 | } 235 | 236 | fn loaded(&self) -> &Self::Loaded { 237 | &() 238 | } 239 | } 240 | 241 | impl> PoseidonInstructions for Pow5T3Chip { 242 | type Word = StateWord; 243 | 244 | fn permute( 245 | &self, 246 | layouter: &mut impl Layouter, 247 | initial_state: &State, 248 | ) -> Result, Error> { 249 | let config = self.config(); 250 | 251 | layouter.assign_region( 252 | || "permute state", 253 | |mut region| { 254 | // Load the initial state into this region. 255 | let state = Pow5T3State::load(&mut region, config, initial_state)?; 256 | 257 | let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| { 258 | res.and_then(|state| state.full_round(&mut region, config, r, r)) 259 | })?; 260 | 261 | let state = (0..config.half_partial_rounds).fold(Ok(state), |res, r| { 262 | res.and_then(|state| { 263 | state.partial_round( 264 | &mut region, 265 | config, 266 | config.half_full_rounds + 2 * r, 267 | config.half_full_rounds + r, 268 | ) 269 | }) 270 | })?; 271 | 272 | let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| { 273 | res.and_then(|state| { 274 | state.full_round( 275 | &mut region, 276 | config, 277 | config.half_full_rounds + 2 * config.half_partial_rounds + r, 278 | config.half_full_rounds + config.half_partial_rounds + r, 279 | ) 280 | }) 281 | })?; 282 | 283 | Ok(state.0) 284 | }, 285 | ) 286 | } 287 | } 288 | 289 | impl> PoseidonDuplexInstructions 290 | for Pow5T3Chip 291 | { 292 | fn initial_state( 293 | &self, 294 | layouter: &mut impl Layouter, 295 | domain: &impl Domain, 296 | ) -> Result, Error> { 297 | let config = self.config(); 298 | layouter.assign_region( 299 | || format!("initial state for domain {:?}", domain), 300 | |mut region| { 301 | let mut load_state_word = |i: usize, value: F| -> Result<_, Error> { 302 | let var = region.assign_advice_from_constant( 303 | || format!("state_{}", i), 304 | config.state[i], 305 | 0, 306 | value, 307 | )?; 308 | Ok(StateWord { 309 | var, 310 | value: Some(value), 311 | }) 312 | }; 313 | 314 | Ok([ 315 | load_state_word(0, F::zero())?, 316 | load_state_word(1, F::zero())?, 317 | load_state_word(2, domain.initial_capacity_element())?, 318 | ]) 319 | }, 320 | ) 321 | } 322 | 323 | fn pad_and_add( 324 | &self, 325 | layouter: &mut impl Layouter, 326 | domain: &impl Domain, 327 | initial_state: &State, 328 | input: &SpongeState, 329 | ) -> Result, Error> { 330 | let config = self.config(); 331 | layouter.assign_region( 332 | || format!("pad-and-add for domain {:?}", domain), 333 | |mut region| { 334 | config.s_pad_and_add.enable(&mut region, 1)?; 335 | 336 | // Load the initial state into this region. 337 | let mut load_state_word = |i: usize| -> Result<_, Error> { 338 | let value = initial_state[i].value; 339 | let var = region.assign_advice( 340 | || format!("load state_{}", i), 341 | config.state[i], 342 | 0, 343 | || value.ok_or(Error::Synthesis), 344 | )?; 345 | region.constrain_equal(initial_state[i].var, var)?; 346 | Ok(StateWord { var, value }) 347 | }; 348 | let initial_state = [ 349 | load_state_word(0)?, 350 | load_state_word(1)?, 351 | load_state_word(2)?, 352 | ]; 353 | 354 | let padding_values = domain.padding(); 355 | 356 | // Load the input and padding into this region. 357 | let mut load_input_word = |i: usize| -> Result<_, Error> { 358 | let (constraint_var, value) = match (input[i], padding_values[i]) { 359 | (Some(word), None) => (word.var, word.value), 360 | (None, Some(padding_value)) => { 361 | let padding_var = region.assign_fixed( 362 | || format!("load pad_{}", i), 363 | config.rc_b[i], 364 | 1, 365 | || Ok(padding_value), 366 | )?; 367 | (padding_var, Some(padding_value)) 368 | } 369 | _ => panic!("Input and padding don't match"), 370 | }; 371 | let var = region.assign_advice( 372 | || format!("load input_{}", i), 373 | config.state[i], 374 | 1, 375 | || value.ok_or(Error::Synthesis), 376 | )?; 377 | region.constrain_equal(constraint_var, var)?; 378 | 379 | Ok(StateWord { var, value }) 380 | }; 381 | let input = [load_input_word(0)?, load_input_word(1)?]; 382 | 383 | // Constrain the output. 384 | let mut constrain_output_word = |i: usize| -> Result<_, Error> { 385 | let value = initial_state[i].value.and_then(|initial_word| { 386 | input 387 | .get(i) 388 | .map(|word| word.value) 389 | // The capacity element is never altered by the input. 390 | .unwrap_or_else(|| Some(F::zero())) 391 | .map(|input_word| initial_word + input_word) 392 | }); 393 | let var = region.assign_advice( 394 | || format!("load output_{}", i), 395 | config.state[i], 396 | 2, 397 | || value.ok_or(Error::Synthesis), 398 | )?; 399 | Ok(StateWord { var, value }) 400 | }; 401 | 402 | Ok([ 403 | constrain_output_word(0)?, 404 | constrain_output_word(1)?, 405 | constrain_output_word(2)?, 406 | ]) 407 | }, 408 | ) 409 | } 410 | 411 | fn get_output(state: &State) -> SpongeState { 412 | [Some(state[0]), Some(state[1])] 413 | } 414 | } 415 | 416 | #[derive(Clone, Copy, Debug)] 417 | pub struct StateWord { 418 | pub var: Cell, 419 | pub value: Option, 420 | } 421 | 422 | impl StateWord { 423 | pub fn new(var: Cell, value: Option) -> Self { 424 | Self { var, value } 425 | } 426 | } 427 | 428 | impl From> for CellValue { 429 | fn from(state_word: StateWord) -> CellValue { 430 | CellValue::new(state_word.var, state_word.value) 431 | } 432 | } 433 | 434 | #[derive(Debug)] 435 | struct Pow5T3State([StateWord; WIDTH]); 436 | 437 | impl Pow5T3State { 438 | fn full_round( 439 | self, 440 | region: &mut Region, 441 | config: &Pow5T3Config, 442 | round: usize, 443 | offset: usize, 444 | ) -> Result { 445 | Self::round(region, config, round, offset, config.s_full, |_| { 446 | let q_0 = self.0[0] 447 | .value 448 | .map(|v| v + config.round_constants[round][0]); 449 | let q_1 = self.0[1] 450 | .value 451 | .map(|v| v + config.round_constants[round][1]); 452 | let q_2 = self.0[2] 453 | .value 454 | .map(|v| v + config.round_constants[round][2]); 455 | 456 | let r_0 = q_0.map(|v| v.pow(&config.alpha)); 457 | let r_1 = q_1.map(|v| v.pow(&config.alpha)); 458 | let r_2 = q_2.map(|v| v.pow(&config.alpha)); 459 | 460 | let m = &config.m_reg; 461 | let r = r_0.and_then(|r_0| r_1.and_then(|r_1| r_2.map(|r_2| [r_0, r_1, r_2]))); 462 | 463 | Ok(( 464 | round + 1, 465 | [ 466 | r.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]), 467 | r.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]), 468 | r.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]), 469 | ], 470 | )) 471 | }) 472 | } 473 | 474 | fn partial_round( 475 | self, 476 | region: &mut Region, 477 | config: &Pow5T3Config, 478 | round: usize, 479 | offset: usize, 480 | ) -> Result { 481 | Self::round(region, config, round, offset, config.s_partial, |region| { 482 | let m = &config.m_reg; 483 | 484 | let p = self.0[0].value.and_then(|p_0| { 485 | self.0[1] 486 | .value 487 | .and_then(|p_1| self.0[2].value.map(|p_2| [p_0, p_1, p_2])) 488 | }); 489 | 490 | let r = p.map(|p| { 491 | [ 492 | (p[0] + config.round_constants[round][0]).pow(&config.alpha), 493 | p[1] + config.round_constants[round][1], 494 | p[2] + config.round_constants[round][2], 495 | ] 496 | }); 497 | 498 | region.assign_advice( 499 | || format!("round_{} partial_sbox", round), 500 | config.partial_sbox, 501 | offset, 502 | || r.map(|r| r[0]).ok_or(Error::Synthesis), 503 | )?; 504 | 505 | let p_mid = r.map(|r| { 506 | [ 507 | m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2], 508 | m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2], 509 | m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2], 510 | ] 511 | }); 512 | 513 | // Load the second round constants. 514 | let mut load_round_constant = |i: usize| { 515 | region.assign_fixed( 516 | || format!("round_{} rc_{}", round + 1, i), 517 | config.rc_b[i], 518 | offset, 519 | || Ok(config.round_constants[round + 1][i]), 520 | ) 521 | }; 522 | for i in 0..WIDTH { 523 | load_round_constant(i)?; 524 | } 525 | 526 | let r_mid = p_mid.map(|p| { 527 | [ 528 | (p[0] + config.round_constants[round + 1][0]).pow(&config.alpha), 529 | p[1] + config.round_constants[round + 1][1], 530 | p[2] + config.round_constants[round + 1][2], 531 | ] 532 | }); 533 | 534 | Ok(( 535 | round + 2, 536 | [ 537 | r_mid.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]), 538 | r_mid.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]), 539 | r_mid.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]), 540 | ], 541 | )) 542 | }) 543 | } 544 | 545 | fn load( 546 | region: &mut Region, 547 | config: &Pow5T3Config, 548 | initial_state: &State, WIDTH>, 549 | ) -> Result { 550 | let mut load_state_word = |i: usize| -> Result<_, Error> { 551 | let value = initial_state[i].value; 552 | let var = region.assign_advice( 553 | || format!("load state_{}", i), 554 | config.state[i], 555 | 0, 556 | || value.ok_or(Error::Synthesis), 557 | )?; 558 | region.constrain_equal(initial_state[i].var, var)?; 559 | Ok(StateWord { var, value }) 560 | }; 561 | 562 | Ok(Pow5T3State([ 563 | load_state_word(0)?, 564 | load_state_word(1)?, 565 | load_state_word(2)?, 566 | ])) 567 | } 568 | 569 | fn round( 570 | region: &mut Region, 571 | config: &Pow5T3Config, 572 | round: usize, 573 | offset: usize, 574 | round_gate: Selector, 575 | round_fn: impl FnOnce(&mut Region) -> Result<(usize, [Option; WIDTH]), Error>, 576 | ) -> Result { 577 | // Enable the required gate. 578 | round_gate.enable(region, offset)?; 579 | 580 | // Load the round constants. 581 | let mut load_round_constant = |i: usize| { 582 | region.assign_fixed( 583 | || format!("round_{} rc_{}", round, i), 584 | config.rc_a[i], 585 | offset, 586 | || Ok(config.round_constants[round][i]), 587 | ) 588 | }; 589 | for i in 0..WIDTH { 590 | load_round_constant(i)?; 591 | } 592 | 593 | // Compute the next round's state. 594 | let (next_round, next_state) = round_fn(region)?; 595 | 596 | let mut next_state_word = |i: usize| -> Result<_, Error> { 597 | let value = next_state[i]; 598 | let var = region.assign_advice( 599 | || format!("round_{} state_{}", next_round, i), 600 | config.state[i], 601 | offset + 1, 602 | || value.ok_or(Error::Synthesis), 603 | )?; 604 | Ok(StateWord { var, value }) 605 | }; 606 | 607 | Ok(Pow5T3State([ 608 | next_state_word(0)?, 609 | next_state_word(1)?, 610 | next_state_word(2)?, 611 | ])) 612 | } 613 | } 614 | 615 | #[cfg(test)] 616 | mod tests { 617 | use crate::halo2::{ 618 | arithmetic::FieldExt, 619 | circuit::{Layouter, SimpleFloorPlanner}, 620 | dev::MockProver, 621 | pasta::Fp, 622 | plonk::{Circuit, ConstraintSystem, Error}, 623 | }; 624 | use ff::PrimeField; 625 | use pasta_curves::pallas; 626 | 627 | use super::{PoseidonInstructions, Pow5T3Chip, Pow5T3Config, StateWord, WIDTH}; 628 | use crate::{ 629 | circuit::gadget::poseidon::{Hash, Word}, 630 | primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec}, 631 | }; 632 | 633 | struct PermuteCircuit {} 634 | 635 | impl Circuit for PermuteCircuit { 636 | type Config = Pow5T3Config; 637 | type FloorPlanner = SimpleFloorPlanner; 638 | 639 | fn without_witnesses(&self) -> Self { 640 | PermuteCircuit {} 641 | } 642 | 643 | fn configure(meta: &mut ConstraintSystem) -> Pow5T3Config { 644 | let state = [ 645 | meta.advice_column(), 646 | meta.advice_column(), 647 | meta.advice_column(), 648 | ]; 649 | let partial_sbox = meta.advice_column(); 650 | 651 | let rc_a = [ 652 | meta.fixed_column(), 653 | meta.fixed_column(), 654 | meta.fixed_column(), 655 | ]; 656 | let rc_b = [ 657 | meta.fixed_column(), 658 | meta.fixed_column(), 659 | meta.fixed_column(), 660 | ]; 661 | 662 | Pow5T3Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b) 663 | } 664 | 665 | fn synthesize( 666 | &self, 667 | config: Pow5T3Config, 668 | mut layouter: impl Layouter, 669 | ) -> Result<(), Error> { 670 | let initial_state = layouter.assign_region( 671 | || "prepare initial state", 672 | |mut region| { 673 | let mut state_word = |i: usize| -> Result<_, Error> { 674 | let value = Some(Fp::from(i as u64)); 675 | let var = region.assign_advice( 676 | || format!("load state_{}", i), 677 | config.state[i], 678 | 0, 679 | || value.ok_or(Error::Synthesis), 680 | )?; 681 | Ok(StateWord { var, value }) 682 | }; 683 | 684 | Ok([state_word(0)?, state_word(1)?, state_word(2)?]) 685 | }, 686 | )?; 687 | 688 | let chip = Pow5T3Chip::construct(config.clone()); 689 | let final_state = as PoseidonInstructions< 690 | Fp, 691 | OrchardNullifier, 692 | WIDTH, 693 | 2, 694 | >>::permute(&chip, &mut layouter, &initial_state)?; 695 | 696 | // For the purpose of this test, compute the real final state inline. 697 | let mut expected_final_state = [Fp::zero(), Fp::one(), Fp::from_u64(2)]; 698 | let (round_constants, mds, _) = OrchardNullifier.constants(); 699 | poseidon::permute::<_, OrchardNullifier, WIDTH, 2>( 700 | &mut expected_final_state, 701 | &mds, 702 | &round_constants, 703 | ); 704 | 705 | layouter.assign_region( 706 | || "constrain final state", 707 | |mut region| { 708 | let mut final_state_word = |i: usize| { 709 | let var = region.assign_advice( 710 | || format!("load final_state_{}", i), 711 | config.state[i], 712 | 0, 713 | || Ok(expected_final_state[i]), 714 | )?; 715 | region.constrain_equal(final_state[i].var, var) 716 | }; 717 | 718 | final_state_word(0)?; 719 | final_state_word(1)?; 720 | final_state_word(2) 721 | }, 722 | ) 723 | } 724 | } 725 | 726 | #[test] 727 | fn poseidon_permute() { 728 | let k = 6; 729 | let circuit = PermuteCircuit {}; 730 | let prover = MockProver::run(k, &circuit, vec![]).unwrap(); 731 | assert_eq!(prover.verify(), Ok(())) 732 | } 733 | 734 | #[derive(Default)] 735 | struct HashCircuit { 736 | message: Option<[Fp; 2]>, 737 | // For the purpose of this test, witness the result. 738 | // TODO: Move this into an instance column. 739 | output: Option, 740 | } 741 | 742 | impl Circuit for HashCircuit { 743 | type Config = Pow5T3Config; 744 | type FloorPlanner = SimpleFloorPlanner; 745 | 746 | fn without_witnesses(&self) -> Self { 747 | Self::default() 748 | } 749 | 750 | fn configure(meta: &mut ConstraintSystem) -> Pow5T3Config { 751 | let state = [ 752 | meta.advice_column(), 753 | meta.advice_column(), 754 | meta.advice_column(), 755 | ]; 756 | let partial_sbox = meta.advice_column(); 757 | 758 | let rc_a = [ 759 | meta.fixed_column(), 760 | meta.fixed_column(), 761 | meta.fixed_column(), 762 | ]; 763 | let rc_b = [ 764 | meta.fixed_column(), 765 | meta.fixed_column(), 766 | meta.fixed_column(), 767 | ]; 768 | 769 | meta.enable_constant(rc_b[0]); 770 | 771 | Pow5T3Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b) 772 | } 773 | 774 | fn synthesize( 775 | &self, 776 | config: Pow5T3Config, 777 | mut layouter: impl Layouter, 778 | ) -> Result<(), Error> { 779 | let chip = Pow5T3Chip::construct(config.clone()); 780 | 781 | let message = layouter.assign_region( 782 | || "load message", 783 | |mut region| { 784 | let mut message_word = |i: usize| -> Result<_, Error> { 785 | let value = self.message.map(|message_vals| message_vals[i]); 786 | let var = region.assign_advice( 787 | || format!("load message_{}", i), 788 | config.state[i], 789 | 0, 790 | || value.ok_or(Error::Synthesis), 791 | )?; 792 | Ok(Word::<_, _, OrchardNullifier, WIDTH, 2> { 793 | inner: StateWord { var, value }, 794 | }) 795 | }; 796 | 797 | Ok([message_word(0)?, message_word(1)?]) 798 | }, 799 | )?; 800 | 801 | let hasher = Hash::init(chip, layouter.namespace(|| "init"), ConstantLength::<2>)?; 802 | let output = hasher.hash(layouter.namespace(|| "hash"), message)?; 803 | 804 | layouter.assign_region( 805 | || "constrain output", 806 | |mut region| { 807 | let expected_var = region.assign_advice( 808 | || "load output", 809 | config.state[0], 810 | 0, 811 | || self.output.ok_or(Error::Synthesis), 812 | )?; 813 | let word: StateWord<_> = output.inner; 814 | region.constrain_equal(word.var, expected_var) 815 | }, 816 | ) 817 | } 818 | } 819 | 820 | #[test] 821 | fn poseidon_hash() { 822 | let message = [Fp::rand(), Fp::rand()]; 823 | let output = poseidon::Hash::init(OrchardNullifier, ConstantLength::<2>).hash(message); 824 | 825 | let k = 6; 826 | let circuit = HashCircuit { 827 | message: Some(message), 828 | output: Some(output), 829 | }; 830 | let prover = MockProver::run(k, &circuit, vec![]).unwrap(); 831 | assert_eq!(prover.verify(), Ok(())) 832 | } 833 | 834 | #[test] 835 | fn hash_test_vectors() { 836 | for tv in crate::primitives::poseidon::test_vectors::fp::hash() { 837 | let message = [ 838 | pallas::Base::from_repr(tv.input[0]).unwrap(), 839 | pallas::Base::from_repr(tv.input[1]).unwrap(), 840 | ]; 841 | let output = poseidon::Hash::init(OrchardNullifier, ConstantLength).hash(message); 842 | 843 | let k = 6; 844 | let circuit = HashCircuit { 845 | message: Some(message), 846 | output: Some(output), 847 | }; 848 | let prover = MockProver::run(k, &circuit, vec![]).unwrap(); 849 | assert_eq!(prover.verify(), Ok(())); 850 | } 851 | } 852 | 853 | #[cfg(feature = "dev-graph")] 854 | #[test] 855 | fn print_poseidon_chip() { 856 | use plotters::prelude::*; 857 | 858 | let root = BitMapBackend::new("poseidon-chip-layout.png", (1024, 768)).into_drawing_area(); 859 | root.fill(&WHITE).unwrap(); 860 | let root = root 861 | .titled("Poseidon Chip Layout", ("sans-serif", 60)) 862 | .unwrap(); 863 | 864 | let circuit = HashCircuit { 865 | message: None, 866 | output: None, 867 | }; 868 | halo2::dev::CircuitLayout::default() 869 | .render(6, &circuit, &root) 870 | .unwrap(); 871 | } 872 | } 873 | -------------------------------------------------------------------------------- /src/circuit/gadget/utilities.rs: -------------------------------------------------------------------------------- 1 | use std::{array, convert::TryInto, ops::Range}; 2 | 3 | use crate::halo2::{ 4 | circuit::{Cell, Layouter, Region}, 5 | plonk::{Advice, Column, Error, Expression}, 6 | }; 7 | use ff::PrimeFieldBits; 8 | use pasta_curves::arithmetic::FieldExt; 9 | 10 | pub(crate) mod cond_swap; 11 | 12 | /// A variable representing a field element. 13 | #[derive(Copy, Clone, Debug)] 14 | pub struct CellValue { 15 | cell: Cell, 16 | value: Option, 17 | } 18 | 19 | pub trait Var: Copy + Clone + std::fmt::Debug { 20 | fn new(cell: Cell, value: Option) -> Self; 21 | fn cell(&self) -> Cell; 22 | fn value(&self) -> Option; 23 | } 24 | 25 | impl Var for CellValue { 26 | fn new(cell: Cell, value: Option) -> Self { 27 | Self { cell, value } 28 | } 29 | 30 | fn cell(&self) -> Cell { 31 | self.cell 32 | } 33 | 34 | fn value(&self) -> Option { 35 | self.value 36 | } 37 | } 38 | 39 | pub trait UtilitiesInstructions { 40 | type Var: Var; 41 | 42 | fn load_private( 43 | &self, 44 | mut layouter: impl Layouter, 45 | column: Column, 46 | value: Option, 47 | ) -> Result { 48 | layouter.assign_region( 49 | || "load private", 50 | |mut region| { 51 | let cell = region.assign_advice( 52 | || "load private", 53 | column, 54 | 0, 55 | || value.ok_or(Error::Synthesis), 56 | )?; 57 | Ok(Var::new(cell, value)) 58 | }, 59 | ) 60 | } 61 | } 62 | 63 | /// Assigns a cell at a specific offset within the given region, constraining it 64 | /// to the same value as another cell (which may be in any region). 65 | /// 66 | /// Returns an error if either `column` or `copy` is not in a column that was passed to 67 | /// [`ConstraintSystem::enable_equality`] during circuit configuration. 68 | /// 69 | /// [`ConstraintSystem::enable_equality`]: halo2::plonk::ConstraintSystem::enable_equality 70 | pub fn copy( 71 | region: &mut Region<'_, F>, 72 | annotation: A, 73 | column: Column, 74 | offset: usize, 75 | copy: &CellValue, 76 | ) -> Result, Error> 77 | where 78 | A: Fn() -> AR, 79 | AR: Into, 80 | { 81 | let cell = region.assign_advice(annotation, column, offset, || { 82 | copy.value.ok_or(Error::Synthesis) 83 | })?; 84 | 85 | region.constrain_equal(cell, copy.cell)?; 86 | 87 | Ok(CellValue::new(cell, copy.value)) 88 | } 89 | 90 | pub fn transpose_option_array( 91 | option_array: Option<[T; LEN]>, 92 | ) -> [Option; LEN] { 93 | let mut ret = [None; LEN]; 94 | if let Some(arr) = option_array { 95 | for (entry, value) in ret.iter_mut().zip(array::IntoIter::new(arr)) { 96 | *entry = Some(value); 97 | } 98 | } 99 | ret 100 | } 101 | 102 | /// Checks that an expresssion is either 1 or 0. 103 | pub fn bool_check(value: Expression) -> Expression { 104 | value.clone() * (Expression::Constant(F::one()) - value) 105 | } 106 | 107 | /// Takes a specified subsequence of the little-endian bit representation of a field element. 108 | /// The bits are numbered from 0 for the LSB. 109 | pub fn bitrange_subset(field_elem: F, bitrange: Range) -> F { 110 | assert!(bitrange.end <= F::NUM_BITS as usize); 111 | 112 | let bits: Vec = field_elem 113 | .to_le_bits() 114 | .iter() 115 | .by_val() 116 | .skip(bitrange.start) 117 | .take(bitrange.end - bitrange.start) 118 | .chain(std::iter::repeat(false)) 119 | .take(256) 120 | .collect(); 121 | let bytearray: Vec = bits 122 | .chunks_exact(8) 123 | .map(|byte| byte.iter().rev().fold(0u8, |acc, bit| acc * 2 + *bit as u8)) 124 | .collect(); 125 | 126 | F::from_bytes(&bytearray.try_into().unwrap()).unwrap() 127 | } 128 | 129 | /// Check that an expression is in the small range [0..range), 130 | /// i.e. 0 ≤ word < range. 131 | pub fn range_check(word: Expression, range: usize) -> Expression { 132 | (1..range).fold(word.clone(), |acc, i| { 133 | acc * (word.clone() - Expression::Constant(F::from_u64(i as u64))) 134 | }) 135 | } 136 | 137 | #[cfg(test)] 138 | mod tests { 139 | use crate::halo2::{ 140 | circuit::{Layouter, SimpleFloorPlanner}, 141 | dev::{MockProver, VerifyFailure}, 142 | plonk::{Circuit, ConstraintSystem, Error, Selector}, 143 | poly::Rotation, 144 | }; 145 | use bigint::U256; 146 | use ff::PrimeField; 147 | use pasta_curves::pallas; 148 | 149 | use super::*; 150 | 151 | #[test] 152 | fn test_range_check() { 153 | struct MyCircuit(u8); 154 | 155 | impl UtilitiesInstructions for MyCircuit { 156 | type Var = CellValue; 157 | } 158 | 159 | #[derive(Clone)] 160 | struct Config { 161 | selector: Selector, 162 | advice: Column, 163 | } 164 | 165 | impl Circuit for MyCircuit { 166 | type Config = Config; 167 | type FloorPlanner = SimpleFloorPlanner; 168 | 169 | fn without_witnesses(&self) -> Self { 170 | MyCircuit(self.0) 171 | } 172 | 173 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 174 | let selector = meta.selector(); 175 | let advice = meta.advice_column(); 176 | 177 | meta.create_gate("range check", |meta| { 178 | let selector = meta.query_selector(selector); 179 | let advice = meta.query_advice(advice, Rotation::cur()); 180 | 181 | vec![selector * range_check(advice, RANGE)] 182 | }); 183 | 184 | Config { selector, advice } 185 | } 186 | 187 | fn synthesize( 188 | &self, 189 | config: Self::Config, 190 | mut layouter: impl Layouter, 191 | ) -> Result<(), Error> { 192 | layouter.assign_region( 193 | || "range constrain", 194 | |mut region| { 195 | config.selector.enable(&mut region, 0)?; 196 | region.assign_advice( 197 | || format!("witness {}", self.0), 198 | config.advice, 199 | 0, 200 | || Ok(pallas::Base::from_u64(self.0.into())), 201 | )?; 202 | 203 | Ok(()) 204 | }, 205 | ) 206 | } 207 | } 208 | 209 | for i in 0..8 { 210 | let circuit: MyCircuit<8> = MyCircuit(i); 211 | let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); 212 | assert_eq!(prover.verify(), Ok(())); 213 | } 214 | 215 | { 216 | let circuit: MyCircuit<8> = MyCircuit(8); 217 | let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); 218 | assert_eq!( 219 | prover.verify(), 220 | Err(vec![VerifyFailure::ConstraintNotSatisfied { 221 | constraint: ((0, "range check").into(), 0, "").into(), 222 | row: 0 223 | }]) 224 | ); 225 | } 226 | } 227 | 228 | #[test] 229 | fn test_bitrange_subset() { 230 | // Subset full range. 231 | { 232 | let field_elem = pallas::Base::rand(); 233 | let bitrange = 0..(pallas::Base::NUM_BITS as usize); 234 | let subset = bitrange_subset(field_elem, bitrange); 235 | assert_eq!(field_elem, subset); 236 | } 237 | 238 | // Subset zero bits 239 | { 240 | let field_elem = pallas::Base::rand(); 241 | let bitrange = 0..0; 242 | let subset = bitrange_subset(field_elem, bitrange); 243 | assert_eq!(pallas::Base::zero(), subset); 244 | } 245 | 246 | // Closure to decompose field element into pieces using consecutive ranges, 247 | // and check that we recover the original. 248 | let decompose = |field_elem: pallas::Base, ranges: &[Range]| { 249 | assert_eq!( 250 | ranges.iter().map(|range| range.len()).sum::(), 251 | pallas::Base::NUM_BITS as usize 252 | ); 253 | assert_eq!(ranges[0].start, 0); 254 | assert_eq!(ranges.last().unwrap().end, pallas::Base::NUM_BITS as usize); 255 | 256 | // Check ranges are contiguous 257 | #[allow(unused_assignments)] 258 | { 259 | let mut ranges = ranges.iter(); 260 | let mut range = ranges.next().unwrap(); 261 | if let Some(next_range) = ranges.next() { 262 | assert_eq!(range.end, next_range.start); 263 | range = next_range; 264 | } 265 | } 266 | 267 | let subsets = ranges 268 | .iter() 269 | .map(|range| bitrange_subset(field_elem, range.clone())) 270 | .collect::>(); 271 | 272 | let mut sum = subsets[0]; 273 | let mut num_bits = 0; 274 | for (idx, subset) in subsets.iter().skip(1).enumerate() { 275 | // 2^num_bits 276 | let range_shift: [u8; 32] = { 277 | num_bits += ranges[idx].len(); 278 | let mut range_shift = [0u8; 32]; 279 | U256([2, 0, 0, 0]) 280 | .pow(U256([num_bits as u64, 0, 0, 0])) 281 | .to_little_endian(&mut range_shift); 282 | range_shift 283 | }; 284 | sum += subset * pallas::Base::from_bytes(&range_shift).unwrap(); 285 | } 286 | assert_eq!(field_elem, sum); 287 | }; 288 | 289 | decompose(pallas::Base::rand(), &[0..255]); 290 | decompose(pallas::Base::rand(), &[0..1, 1..255]); 291 | decompose(pallas::Base::rand(), &[0..254, 254..255]); 292 | decompose(pallas::Base::rand(), &[0..127, 127..255]); 293 | decompose(pallas::Base::rand(), &[0..128, 128..255]); 294 | decompose( 295 | pallas::Base::rand(), 296 | &[0..50, 50..100, 100..150, 150..200, 200..255], 297 | ); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/circuit/gadget/utilities/cond_swap.rs: -------------------------------------------------------------------------------- 1 | use std::{array, marker::PhantomData}; 2 | 3 | use crate::halo2::{ 4 | circuit::{Chip, Layouter}, 5 | plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, 6 | poly::Rotation, 7 | }; 8 | use pasta_curves::arithmetic::FieldExt; 9 | 10 | use crate::circuit::gadget::utilities::{copy, CellValue, UtilitiesInstructions, Var}; 11 | 12 | pub trait CondSwapInstructions: UtilitiesInstructions { 13 | #[allow(clippy::type_complexity)] 14 | /// Given an input pair (a,b) and a `swap` boolean flag, returns 15 | /// (b,a) if `swap` is set, else (a,b) if `swap` is not set. 16 | /// 17 | /// The second element of the pair is required to be a witnessed 18 | /// value, not a variable that already exists in the circuit. 19 | fn swap( 20 | &self, 21 | layouter: impl Layouter, 22 | pair: (Self::Var, Self::Var), 23 | swap: Option, 24 | ) -> Result<(Self::Var, Self::Var), Error>; 25 | } 26 | 27 | /// A chip implementing a conditional swap. 28 | #[derive(Clone, Debug)] 29 | pub struct CondSwapChip { 30 | config: CondSwapConfig, 31 | _marker: PhantomData, 32 | } 33 | 34 | impl Chip for CondSwapChip { 35 | type Config = CondSwapConfig; 36 | type Loaded = (); 37 | 38 | fn config(&self) -> &Self::Config { 39 | &self.config 40 | } 41 | 42 | fn loaded(&self) -> &Self::Loaded { 43 | &() 44 | } 45 | } 46 | 47 | #[derive(Clone, Debug)] 48 | pub struct CondSwapConfig { 49 | pub q_swap: Selector, 50 | pub a: Column, 51 | pub b: Column, 52 | pub a_swapped: Column, 53 | pub b_swapped: Column, 54 | pub swap: Column, 55 | } 56 | 57 | impl UtilitiesInstructions for CondSwapChip { 58 | type Var = CellValue; 59 | } 60 | 61 | impl CondSwapInstructions for CondSwapChip { 62 | #[allow(clippy::type_complexity)] 63 | fn swap( 64 | &self, 65 | mut layouter: impl Layouter, 66 | pair: (Self::Var, Self::Var), 67 | swap: Option, 68 | ) -> Result<(Self::Var, Self::Var), Error> { 69 | let config = self.config(); 70 | 71 | layouter.assign_region( 72 | || "swap", 73 | |mut region| { 74 | // Enable `q_swap` selector 75 | config.q_swap.enable(&mut region, 0)?; 76 | 77 | // Copy in `a` value 78 | let a = copy(&mut region, || "copy a", config.a, 0, &pair.0)?; 79 | let b = copy(&mut region, || "copy b", config.b, 0, &pair.1)?; 80 | // Witness `swap` value 81 | let swap_val = swap.map(|swap| F::from_u64(swap as u64)); 82 | 83 | region.assign_advice( 84 | || "swap", 85 | config.swap, 86 | 0, 87 | || swap_val.ok_or(Error::Synthesis), 88 | )?; 89 | 90 | // Conditionally swap a 91 | let a_swapped = { 92 | let a_swapped = 93 | a.value() 94 | .zip(b.value()) 95 | .zip(swap) 96 | .map(|((a, b), swap)| if swap { b } else { a }); 97 | let a_swapped_cell = region.assign_advice( 98 | || "a_swapped", 99 | config.a_swapped, 100 | 0, 101 | || a_swapped.ok_or(Error::Synthesis), 102 | )?; 103 | CellValue::new(a_swapped_cell, a_swapped) 104 | }; 105 | 106 | // Conditionally swap b 107 | let b_swapped = { 108 | let b_swapped = 109 | a.value() 110 | .zip(b.value()) 111 | .zip(swap) 112 | .map(|((a, b), swap)| if swap { a } else { b }); 113 | let b_swapped_cell = region.assign_advice( 114 | || "b_swapped", 115 | config.b_swapped, 116 | 0, 117 | || b_swapped.ok_or(Error::Synthesis), 118 | )?; 119 | CellValue::new(b_swapped_cell, b_swapped) 120 | }; 121 | 122 | // Return swapped pair 123 | Ok((a_swapped, b_swapped)) 124 | }, 125 | ) 126 | } 127 | } 128 | 129 | impl CondSwapChip { 130 | /// Configures this chip for use in a circuit. 131 | /// 132 | /// # Side-effects 133 | /// 134 | /// `advices[0]` will be equality-enabled. 135 | pub fn configure( 136 | meta: &mut ConstraintSystem, 137 | advices: [Column; 5], 138 | ) -> CondSwapConfig { 139 | // Only column a is used in an equality constraint directly by this chip. 140 | let q_swap = meta.selector(); 141 | 142 | meta.enable_equality(advices[0].into()); 143 | meta.enable_equality(advices[1].into()); 144 | let config = CondSwapConfig { 145 | q_swap, 146 | a: advices[0], 147 | b: advices[1], 148 | a_swapped: advices[2], 149 | b_swapped: advices[3], 150 | swap: advices[4], 151 | }; 152 | 153 | // TODO: optimise shape of gate for Merkle path validation 154 | 155 | meta.create_gate("a' = b ⋅ swap + a ⋅ (1-swap)", |meta| { 156 | let q_swap = meta.query_selector(q_swap); 157 | 158 | let a = meta.query_advice(config.a, Rotation::cur()); 159 | let b = meta.query_advice(config.b, Rotation::cur()); 160 | 161 | let a_swapped = meta.query_advice(config.a_swapped, Rotation::cur()); 162 | let b_swapped = meta.query_advice(config.b_swapped, Rotation::cur()); 163 | let swap = meta.query_advice(config.swap, Rotation::cur()); 164 | 165 | let one = Expression::Constant(F::one()); 166 | 167 | // a_swapped - b ⋅ swap - a ⋅ (1-swap) = 0 168 | // This checks that `a_swapped` is equal to `y` when `swap` is set, 169 | // but remains as `a` when `swap` is not set. 170 | let a_check = 171 | a_swapped - b.clone() * swap.clone() - a.clone() * (one.clone() - swap.clone()); 172 | 173 | // b_swapped - a ⋅ swap - b ⋅ (1-swap) = 0 174 | // This checks that `b_swapped` is equal to `a` when `swap` is set, 175 | // but remains as `b` when `swap` is not set. 176 | let b_check = b_swapped - a * swap.clone() - b * (one.clone() - swap.clone()); 177 | 178 | // Check `swap` is boolean. 179 | let bool_check = swap.clone() * (one - swap); 180 | 181 | array::IntoIter::new([a_check, b_check, bool_check]) 182 | .map(move |poly| q_swap.clone() * poly) 183 | }); 184 | 185 | config 186 | } 187 | 188 | pub fn construct(config: CondSwapConfig) -> Self { 189 | CondSwapChip { 190 | config, 191 | _marker: PhantomData, 192 | } 193 | } 194 | } 195 | 196 | #[cfg(test)] 197 | mod tests { 198 | use crate::halo2::{ 199 | circuit::{Layouter, SimpleFloorPlanner}, 200 | dev::MockProver, 201 | plonk::{Circuit, ConstraintSystem, Error}, 202 | }; 203 | use pasta_curves::{arithmetic::FieldExt, pallas::Base}; 204 | 205 | use super::*; 206 | use super::{CondSwapChip, CondSwapConfig, CondSwapInstructions}; 207 | 208 | #[test] 209 | fn cond_swap() { 210 | #[derive(Default)] 211 | struct MyCircuit { 212 | a: Option, 213 | b: Option, 214 | swap: Option, 215 | } 216 | 217 | impl Circuit for MyCircuit { 218 | type Config = CondSwapConfig; 219 | type FloorPlanner = SimpleFloorPlanner; 220 | 221 | fn without_witnesses(&self) -> Self { 222 | Self::default() 223 | } 224 | 225 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 226 | let advices = [ 227 | meta.advice_column(), 228 | meta.advice_column(), 229 | meta.advice_column(), 230 | meta.advice_column(), 231 | meta.advice_column(), 232 | ]; 233 | 234 | CondSwapChip::::configure(meta, advices) 235 | } 236 | 237 | fn synthesize( 238 | &self, 239 | config: Self::Config, 240 | mut layouter: impl Layouter, 241 | ) -> Result<(), Error> { 242 | let chip = CondSwapChip::::construct(config.clone()); 243 | 244 | // Load the pair and the swap flag into the circuit. 245 | let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?; 246 | let b = chip.load_private(layouter.namespace(|| "b"), config.b, self.b)?; 247 | // Return the swapped pair. 248 | let swapped_pair = chip.swap(layouter.namespace(|| "swap"), (a, b), self.swap)?; 249 | 250 | if let Some(swap) = self.swap { 251 | if swap { 252 | // Check that `a` and `b` have been swapped 253 | assert_eq!(swapped_pair.0.value().unwrap(), self.b.unwrap()); 254 | assert_eq!(swapped_pair.1.value().unwrap(), a.value().unwrap()); 255 | } else { 256 | // Check that `a` and `b` have not been swapped 257 | assert_eq!(swapped_pair.0.value().unwrap(), a.value().unwrap()); 258 | assert_eq!(swapped_pair.1.value().unwrap(), self.b.unwrap()); 259 | } 260 | } 261 | 262 | Ok(()) 263 | } 264 | } 265 | 266 | // Test swap case 267 | { 268 | let circuit: MyCircuit = MyCircuit { 269 | a: Some(Base::rand()), 270 | b: Some(Base::rand()), 271 | swap: Some(true), 272 | }; 273 | let prover = 274 | MockProver::::run(4, &circuit, vec![]).expect("mock should not fail"); 275 | assert_eq!(prover.verify(), Ok(())); 276 | } 277 | 278 | // Test non-swap case 279 | { 280 | let circuit: MyCircuit = MyCircuit { 281 | a: Some(Base::rand()), 282 | b: Some(Base::rand()), 283 | swap: Some(false), 284 | }; 285 | let prover = MockProver::::run(4, &circuit, vec![]).unwrap(); 286 | assert_eq!(prover.verify(), Ok(())); 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/circuit/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod gadget; 2 | -------------------------------------------------------------------------------- /src/franchise.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::halo2::{ 4 | circuit::{Layouter, SimpleFloorPlanner}, 5 | pasta::Fp, 6 | plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance}, 7 | }; 8 | 9 | use crate::circuit::gadget::poseidon::{Hash, Pow5T3Chip, Pow5T3Config, StateWord, Word}; 10 | use crate::circuit::gadget::utilities::cond_swap::{ 11 | CondSwapChip, CondSwapConfig, CondSwapInstructions, 12 | }; 13 | use crate::circuit::gadget::utilities::{CellValue, Var}; 14 | use crate::primitives::poseidon::{ConstantLength, P128Pow5T3}; 15 | 16 | #[derive(Clone, Default)] 17 | pub struct FranchiseCircuit { 18 | pub pri_index: Option<[bool; LVL]>, 19 | pub pri_siblings: Option<[Fp; LVL]>, 20 | pub pri_secret_key: Option, 21 | pub pub_processid: Option<[Fp; 2]>, 22 | pub pub_votehash: Option, 23 | } 24 | 25 | #[derive(Clone)] 26 | pub struct FranchiseConfig { 27 | hash: Pow5T3Config, 28 | swap: CondSwapConfig, 29 | instance: Column, 30 | } 31 | 32 | impl FranchiseCircuit { 33 | fn hash( 34 | &self, 35 | config: &FranchiseConfig, 36 | mut layouter: impl Layouter, 37 | values: [CellValue; 2], 38 | ) -> Result, Error> { 39 | let hash_chip = Pow5T3Chip::construct(config.hash.clone()); 40 | 41 | let hasher: Hash< 42 | Fp, 43 | Pow5T3Chip, 44 | P128Pow5T3, 45 | ConstantLength<2_usize>, 46 | 3_usize, 47 | 2_usize, 48 | > = Hash::init( 49 | hash_chip, 50 | layouter.namespace(|| "init"), 51 | ConstantLength::<2>, 52 | )?; 53 | 54 | let v0 = Word::from_inner(StateWord::new(values[0].cell(), values[0].value())); 55 | let v1 = Word::from_inner(StateWord::new(values[1].cell(), values[1].value())); 56 | 57 | let hashed = hasher.hash(layouter.namespace(|| "hash"), [v0, v1])?; 58 | 59 | let cell_value = CellValue::new(hashed.inner().var, hashed.inner().value); 60 | 61 | Ok(cell_value) 62 | } 63 | 64 | fn merkle_tree( 65 | &self, 66 | config: &FranchiseConfig, 67 | mut layouter: impl Layouter, 68 | mut root: CellValue, 69 | ) -> Result, Error> { 70 | for n in 0..LVL { 71 | let leaf = self.load_private_input( 72 | layouter.namespace(|| "load witness"), 73 | config.swap.b, 74 | self.pri_siblings.map(|v| v[n]), 75 | )?; 76 | 77 | let swap_chip = CondSwapChip::::construct(config.swap.clone()); 78 | 79 | let (left, right) = swap_chip.swap( 80 | layouter.namespace(|| "mt swap"), 81 | (root, leaf), 82 | self.pri_index.map(|v| v[n]), 83 | )?; 84 | 85 | root = self.hash(config, layouter.namespace(|| "mt hash"), [left, right])?; 86 | } 87 | 88 | Ok(root) 89 | } 90 | 91 | fn load_constant( 92 | &self, 93 | config: &FranchiseConfig, 94 | mut layouter: impl Layouter, 95 | constant: Fp, 96 | ) -> Result, Error> { 97 | let mut num = None; 98 | 99 | layouter.assign_region( 100 | || "load constant", 101 | |mut region| { 102 | let cell = region.assign_advice_from_constant( 103 | || "constant value", 104 | config.swap.a, // column 105 | 0, // offset 106 | constant, // value 107 | )?; 108 | num = Some(CellValue::new(cell, Some(constant))); 109 | 110 | Ok(()) 111 | }, 112 | )?; 113 | Ok(num.unwrap()) 114 | } 115 | 116 | fn load_private_input( 117 | &self, 118 | mut layouter: impl Layouter, 119 | column: Column, 120 | value: Option, 121 | ) -> Result, Error> { 122 | let cell = layouter.assign_region( 123 | || "load private input", 124 | |mut region| { 125 | let cell = region.assign_advice( 126 | || String::from("load private"), 127 | column, 128 | 0, 129 | || value.ok_or(Error::Synthesis), 130 | )?; 131 | 132 | Ok(CellValue::new(cell, value)) 133 | }, 134 | )?; 135 | 136 | Ok(cell) 137 | } 138 | } 139 | 140 | impl Circuit for FranchiseCircuit { 141 | type Config = FranchiseConfig; 142 | type FloorPlanner = SimpleFloorPlanner; 143 | 144 | fn without_witnesses(&self) -> Self { 145 | Self::default() 146 | } 147 | 148 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 149 | let state = [ 150 | meta.advice_column(), 151 | meta.advice_column(), 152 | meta.advice_column(), 153 | ]; 154 | let partial_sbox = meta.advice_column(); 155 | 156 | let rc_a = [ 157 | meta.fixed_column(), 158 | meta.fixed_column(), 159 | meta.fixed_column(), 160 | ]; 161 | 162 | let rc_b = [ 163 | meta.fixed_column(), 164 | meta.fixed_column(), 165 | meta.fixed_column(), 166 | ]; 167 | 168 | meta.enable_constant(rc_b[0]); 169 | 170 | let swap_advices = [ 171 | meta.advice_column(), 172 | meta.advice_column(), 173 | meta.advice_column(), 174 | meta.advice_column(), 175 | meta.advice_column(), 176 | ]; 177 | 178 | for s_a in swap_advices { 179 | meta.enable_equality(s_a.into()); 180 | } 181 | 182 | let instance = meta.instance_column(); 183 | meta.enable_equality(instance.into()); 184 | 185 | Self::Config { 186 | swap: CondSwapChip::configure(meta, swap_advices), 187 | hash: Pow5T3Chip::configure(meta, P128Pow5T3, state, partial_sbox, rc_a, rc_b), 188 | instance, 189 | } 190 | } 191 | 192 | fn synthesize( 193 | &self, 194 | config: Self::Config, 195 | mut layouter: impl Layouter, 196 | ) -> Result<(), Error> { 197 | let one = self.load_constant(&config, layouter.namespace(|| "load ONE"), Fp::one())?; 198 | 199 | let process_id_0 = self.load_private_input( 200 | layouter.namespace(|| "load process_id[0]"), 201 | config.swap.a, 202 | self.pub_processid.map(|v| v[0]), 203 | )?; 204 | 205 | let process_id_1 = self.load_private_input( 206 | layouter.namespace(|| "load process_id[1]"), 207 | config.swap.a, 208 | self.pub_processid.map(|v| v[1]), 209 | )?; 210 | 211 | let secret_key = self.load_private_input( 212 | layouter.namespace(|| "load secret key"), 213 | config.swap.a, 214 | self.pri_secret_key, 215 | )?; 216 | 217 | let vote_hash = self.load_private_input( 218 | layouter.namespace(|| "load vote hash"), 219 | config.swap.a, 220 | self.pub_votehash, 221 | )?; 222 | 223 | let public_key = self.hash( 224 | &config, 225 | layouter.namespace(|| "hash secret key"), 226 | [one, secret_key], 227 | )?; 228 | 229 | let process_id_hash = self.hash( 230 | &config, 231 | layouter.namespace(|| "hash process_id"), 232 | [process_id_0, process_id_1], 233 | )?; 234 | 235 | let nullifier = self.hash( 236 | &config, 237 | layouter.namespace(|| "nullifier"), 238 | [secret_key, process_id_hash], 239 | )?; 240 | 241 | let root = self.merkle_tree(&config, layouter.namespace(|| "mt"), public_key)?; 242 | 243 | // expose census root as public_input[0] 244 | layouter.constrain_instance(root.cell(), config.instance, 0)?; 245 | 246 | // expose nullifier as public_input[1] 247 | layouter.constrain_instance(nullifier.cell(), config.instance, 1)?; 248 | 249 | // expose vote hash public_input[2] 250 | layouter.constrain_instance(vote_hash.cell(), config.instance, 2)?; 251 | 252 | Ok(()) 253 | } 254 | } 255 | 256 | #[cfg(test)] 257 | mod test { 258 | use crate::halo2::dev::CircuitLayout; 259 | use crate::halo2::dev::MockProver; 260 | use crate::halo2::pasta::Fp; 261 | use plotters::prelude::*; 262 | 263 | use super::*; 264 | use crate::utils::generate_test_data; 265 | 266 | fn print_circuit(circuit: FranchiseCircuit, k: u32) { 267 | let root = BitMapBackend::new("circuit-layout.png", (1024, 768)).into_drawing_area(); 268 | root.fill(&WHITE).unwrap(); 269 | let root = root.titled("Circuit Layout", ("sans-serif", 6)).unwrap(); 270 | 271 | CircuitLayout::default() 272 | .render(k as usize, &circuit, &root) 273 | .unwrap(); 274 | } 275 | 276 | fn mock_test(k: u32) { 277 | let (circuit, mut public) = generate_test_data::(); 278 | 279 | let prover = MockProver::run(k, &circuit, vec![public.clone()]).expect("cannot run mock"); 280 | assert_eq!(Ok(()), prover.verify()); 281 | 282 | for n in 0..public.len() { 283 | public[n] += Fp::from(1); 284 | assert!(MockProver::run(k, &circuit, vec![public.clone()]) 285 | .expect("cannot run mock") 286 | .verify() 287 | .is_err()); 288 | public[n] -= Fp::from(1); 289 | } 290 | } 291 | 292 | #[test] 293 | fn test_franchise() { 294 | mock_test::<3>(8); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[cfg(not(feature = "wasm"))] 4 | pub use halo2_zcash as halo2; 5 | 6 | #[cfg(feature = "wasm")] 7 | pub use halo2_adria0 as halo2; 8 | 9 | mod circuit; 10 | pub mod franchise; 11 | mod primitives; 12 | pub mod utils; 13 | -------------------------------------------------------------------------------- /src/primitives/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod poseidon; 2 | -------------------------------------------------------------------------------- /src/primitives/poseidon.rs: -------------------------------------------------------------------------------- 1 | //! The Poseidon algebraic hash function. 2 | 3 | use std::array; 4 | use std::fmt; 5 | use std::iter; 6 | use std::marker::PhantomData; 7 | 8 | use crate::halo2::arithmetic::FieldExt; 9 | 10 | pub(crate) mod fp; 11 | pub(crate) mod fq; 12 | pub(crate) mod grain; 13 | pub(crate) mod mds; 14 | 15 | #[cfg(test)] 16 | pub(crate) mod test_vectors; 17 | 18 | #[allow(missing_docs)] 19 | mod p128pow5t3; 20 | use grain::SboxType; 21 | pub use p128pow5t3::P128Pow5T3; 22 | 23 | /// The type used to hold permutation state. 24 | pub(crate) type State = [F; T]; 25 | 26 | /// The type used to hold duplex sponge state. 27 | pub(crate) type SpongeState = [Option; RATE]; 28 | 29 | /// The type used to hold the MDS matrix and its inverse. 30 | pub(crate) type Mds = [[F; T]; T]; 31 | 32 | /// A specification for a Poseidon permutation. 33 | pub trait Spec { 34 | /// The number of full rounds for this specification. 35 | /// 36 | /// This must be an even number. 37 | fn full_rounds() -> usize; 38 | 39 | /// The number of partial rounds for this specification. 40 | fn partial_rounds() -> usize; 41 | 42 | /// The S-box for this specification. 43 | fn sbox(val: F) -> F; 44 | 45 | /// Side-loaded index of the first correct and secure MDS that will be generated by 46 | /// the reference implementation. 47 | /// 48 | /// This is used by the default implementation of [`Spec::constants`]. If you are 49 | /// hard-coding the constants, you may leave this unimplemented. 50 | fn secure_mds(&self) -> usize; 51 | 52 | /// Generates `(round_constants, mds, mds^-1)` corresponding to this specification. 53 | fn constants(&self) -> (Vec<[F; T]>, Mds, Mds) { 54 | let r_f = Self::full_rounds(); 55 | let r_p = Self::partial_rounds(); 56 | 57 | let mut grain = grain::Grain::new(SboxType::Pow, T as u16, r_f as u16, r_p as u16); 58 | 59 | let round_constants = (0..(r_f + r_p)) 60 | .map(|_| { 61 | let mut rc_row = [F::zero(); T]; 62 | for (rc, value) in rc_row 63 | .iter_mut() 64 | .zip((0..T).map(|_| grain.next_field_element())) 65 | { 66 | *rc = value; 67 | } 68 | rc_row 69 | }) 70 | .collect(); 71 | 72 | let (mds, mds_inv) = mds::generate_mds::(&mut grain, self.secure_mds()); 73 | 74 | (round_constants, mds, mds_inv) 75 | } 76 | } 77 | 78 | /// Runs the Poseidon permutation on the given state. 79 | pub(crate) fn permute, const T: usize, const RATE: usize>( 80 | state: &mut State, 81 | mds: &Mds, 82 | round_constants: &[[F; T]], 83 | ) { 84 | let r_f = S::full_rounds() / 2; 85 | let r_p = S::partial_rounds(); 86 | 87 | let apply_mds = |state: &mut State| { 88 | let mut new_state = [F::zero(); T]; 89 | // Matrix multiplication 90 | #[allow(clippy::needless_range_loop)] 91 | for i in 0..T { 92 | for j in 0..T { 93 | new_state[i] += mds[i][j] * state[j]; 94 | } 95 | } 96 | *state = new_state; 97 | }; 98 | 99 | let full_round = |state: &mut State, rcs: &[F; T]| { 100 | for (word, rc) in state.iter_mut().zip(rcs.iter()) { 101 | *word = S::sbox(*word + rc); 102 | } 103 | apply_mds(state); 104 | }; 105 | 106 | let part_round = |state: &mut State, rcs: &[F; T]| { 107 | for (word, rc) in state.iter_mut().zip(rcs.iter()) { 108 | *word += rc; 109 | } 110 | // In a partial round, the S-box is only applied to the first state word. 111 | state[0] = S::sbox(state[0]); 112 | apply_mds(state); 113 | }; 114 | 115 | iter::empty() 116 | .chain(iter::repeat(&full_round as &dyn Fn(&mut State, &[F; T])).take(r_f)) 117 | .chain(iter::repeat(&part_round as &dyn Fn(&mut State, &[F; T])).take(r_p)) 118 | .chain(iter::repeat(&full_round as &dyn Fn(&mut State, &[F; T])).take(r_f)) 119 | .zip(round_constants.iter()) 120 | .fold(state, |state, (round, rcs)| { 121 | round(state, rcs); 122 | state 123 | }); 124 | } 125 | 126 | fn poseidon_duplex, const T: usize, const RATE: usize>( 127 | state: &mut State, 128 | input: &SpongeState, 129 | pad_and_add: &dyn Fn(&mut State, &SpongeState), 130 | mds_matrix: &Mds, 131 | round_constants: &[[F; T]], 132 | ) -> SpongeState { 133 | pad_and_add(state, input); 134 | 135 | permute::(state, mds_matrix, round_constants); 136 | 137 | let mut output = [None; RATE]; 138 | for (word, value) in output.iter_mut().zip(state.iter()) { 139 | *word = Some(*value); 140 | } 141 | output 142 | } 143 | 144 | pub(crate) enum Sponge { 145 | Absorbing(SpongeState), 146 | Squeezing(SpongeState), 147 | } 148 | 149 | impl Sponge { 150 | pub(crate) fn absorb(val: F) -> Self { 151 | let mut input = [None; RATE]; 152 | input[0] = Some(val); 153 | Sponge::Absorbing(input) 154 | } 155 | } 156 | 157 | /// A Poseidon duplex sponge. 158 | pub(crate) struct Duplex, const T: usize, const RATE: usize> { 159 | sponge: Sponge, 160 | state: State, 161 | pad_and_add: Box, &SpongeState)>, 162 | mds_matrix: Mds, 163 | round_constants: Vec<[F; T]>, 164 | _marker: PhantomData, 165 | } 166 | 167 | impl, const T: usize, const RATE: usize> Duplex { 168 | /// Constructs a new duplex sponge for the given Poseidon specification. 169 | pub(crate) fn new( 170 | spec: S, 171 | initial_capacity_element: F, 172 | pad_and_add: Box, &SpongeState)>, 173 | ) -> Self { 174 | let (round_constants, mds_matrix, _) = spec.constants(); 175 | 176 | let input = [None; RATE]; 177 | let mut state = [F::zero(); T]; 178 | state[RATE] = initial_capacity_element; 179 | 180 | Duplex { 181 | sponge: Sponge::Absorbing(input), 182 | state, 183 | pad_and_add, 184 | mds_matrix, 185 | round_constants, 186 | _marker: PhantomData::default(), 187 | } 188 | } 189 | 190 | /// Absorbs an element into the sponge. 191 | pub(crate) fn absorb(&mut self, value: F) { 192 | match self.sponge { 193 | Sponge::Absorbing(ref mut input) => { 194 | for entry in input.iter_mut() { 195 | if entry.is_none() { 196 | *entry = Some(value); 197 | return; 198 | } 199 | } 200 | 201 | // We've already absorbed as many elements as we can 202 | let _ = poseidon_duplex::( 203 | &mut self.state, 204 | input, 205 | &self.pad_and_add, 206 | &self.mds_matrix, 207 | &self.round_constants, 208 | ); 209 | self.sponge = Sponge::absorb(value); 210 | } 211 | Sponge::Squeezing(_) => { 212 | // Drop the remaining output elements 213 | self.sponge = Sponge::absorb(value); 214 | } 215 | } 216 | } 217 | 218 | /// Squeezes an element from the sponge. 219 | pub(crate) fn squeeze(&mut self) -> F { 220 | loop { 221 | match self.sponge { 222 | Sponge::Absorbing(ref input) => { 223 | self.sponge = Sponge::Squeezing(poseidon_duplex::( 224 | &mut self.state, 225 | input, 226 | &self.pad_and_add, 227 | &self.mds_matrix, 228 | &self.round_constants, 229 | )); 230 | } 231 | Sponge::Squeezing(ref mut output) => { 232 | for entry in output.iter_mut() { 233 | if let Some(e) = entry.take() { 234 | return e; 235 | } 236 | } 237 | 238 | // We've already squeezed out all available elements 239 | self.sponge = Sponge::Absorbing([None; RATE]); 240 | } 241 | } 242 | } 243 | } 244 | } 245 | 246 | /// A domain in which a Poseidon hash function is being used. 247 | pub trait Domain: Copy + fmt::Debug { 248 | /// The initial capacity element, encoding this domain. 249 | fn initial_capacity_element(&self) -> F; 250 | 251 | /// The padding that will be added to each state word by [`Domain::pad_and_add`]. 252 | fn padding(&self) -> SpongeState; 253 | 254 | /// Returns a function that will update the given state with the given input to a 255 | /// duplex permutation round, applying padding according to this domain specification. 256 | fn pad_and_add(&self) -> Box, &SpongeState)>; 257 | } 258 | 259 | /// A Poseidon hash function used with constant input length. 260 | /// 261 | /// Domain specified in section 4.2 of https://eprint.iacr.org/2019/458.pdf 262 | #[derive(Clone, Copy, Debug)] 263 | pub struct ConstantLength; 264 | 265 | impl Domain 266 | for ConstantLength 267 | { 268 | fn initial_capacity_element(&self) -> F { 269 | // Capacity value is $length \cdot 2^64 + (o-1)$ where o is the output length. 270 | // We hard-code an output length of 1. 271 | F::from_u128((L as u128) << 64) 272 | } 273 | 274 | fn padding(&self) -> SpongeState { 275 | // For constant-input-length hashing, padding consists of the field elements being 276 | // zero. 277 | let mut padding = [None; RATE]; 278 | for word in padding.iter_mut().skip(L) { 279 | *word = Some(F::zero()); 280 | } 281 | padding 282 | } 283 | 284 | fn pad_and_add(&self) -> Box, &SpongeState)> { 285 | Box::new(|state, input| { 286 | // `Iterator::zip` short-circuits when one iterator completes, so this will only 287 | // mutate the rate portion of the state. 288 | for (word, value) in state.iter_mut().zip(input.iter()) { 289 | // For constant-input-length hashing, padding consists of the field 290 | // elements being zero, so we don't add anything to the state. 291 | if let Some(value) = value { 292 | *word += value; 293 | } 294 | } 295 | }) 296 | } 297 | } 298 | 299 | /// A Poseidon hash function, built around a duplex sponge. 300 | pub struct Hash< 301 | F: FieldExt, 302 | S: Spec, 303 | D: Domain, 304 | const T: usize, 305 | const RATE: usize, 306 | > { 307 | duplex: Duplex, 308 | domain: D, 309 | } 310 | 311 | impl< 312 | F: FieldExt, 313 | S: Spec, 314 | D: Domain, 315 | const T: usize, 316 | const RATE: usize, 317 | > fmt::Debug for Hash 318 | { 319 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 320 | f.debug_struct("Hash") 321 | .field("width", &T) 322 | .field("rate", &RATE) 323 | .field("R_F", &S::full_rounds()) 324 | .field("R_P", &S::partial_rounds()) 325 | .field("domain", &self.domain) 326 | .finish() 327 | } 328 | } 329 | 330 | impl< 331 | F: FieldExt, 332 | S: Spec, 333 | D: Domain, 334 | const T: usize, 335 | const RATE: usize, 336 | > Hash 337 | { 338 | /// Initializes a new hasher. 339 | pub fn init(spec: S, domain: D) -> Self { 340 | Hash { 341 | duplex: Duplex::new( 342 | spec, 343 | domain.initial_capacity_element(), 344 | domain.pad_and_add(), 345 | ), 346 | domain, 347 | } 348 | } 349 | } 350 | 351 | impl, const T: usize, const RATE: usize, const L: usize> 352 | Hash, T, RATE> 353 | { 354 | /// Hashes the given input. 355 | pub fn hash(mut self, message: [F; L]) -> F { 356 | for value in array::IntoIter::new(message) { 357 | self.duplex.absorb(value); 358 | } 359 | self.duplex.squeeze() 360 | } 361 | } 362 | 363 | #[cfg(test)] 364 | mod tests { 365 | use crate::halo2::arithmetic::FieldExt; 366 | use pasta_curves::pallas; 367 | 368 | use super::{permute, ConstantLength, Hash, P128Pow5T3 as OrchardNullifier, Spec}; 369 | 370 | #[test] 371 | fn orchard_spec_equivalence() { 372 | let message = [pallas::Base::from_u64(6), pallas::Base::from_u64(42)]; 373 | 374 | let (round_constants, mds, _) = OrchardNullifier.constants(); 375 | 376 | let hasher = Hash::init(OrchardNullifier, ConstantLength); 377 | let result = hasher.hash(message); 378 | 379 | // The result should be equivalent to just directly applying the permutation and 380 | // taking the first state element as the output. 381 | let mut state = [message[0], message[1], pallas::Base::from_u128(2 << 64)]; 382 | permute::<_, OrchardNullifier, 3, 2>(&mut state, &mds, &round_constants); 383 | assert_eq!(state[0], result); 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/primitives/poseidon/fq.rs: -------------------------------------------------------------------------------- 1 | //! https://github.com/daira/pasta-hadeshash 2 | //! 3 | //! sage generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 4 | use pasta_curves::vesta; 5 | 6 | // Number of round constants: 192 7 | // Round constants for GF(p): 8 | pub(crate) const ROUND_CONSTANTS: [[vesta::Base; 3]; 64] = [ 9 | [ 10 | vesta::Base::from_raw([ 11 | 0x5753_8c25_9642_6303, 12 | 0x4e71_162f_3100_3b70, 13 | 0x353f_628f_76d1_10f3, 14 | 0x360d_7470_611e_473d, 15 | ]), 16 | vesta::Base::from_raw([ 17 | 0xbdb7_4213_bf63_188b, 18 | 0x4908_ac2f_12eb_e06f, 19 | 0x5dc3_c6c5_febf_aa31, 20 | 0x2bab_94d7_ae22_2d13, 21 | ]), 22 | vesta::Base::from_raw([ 23 | 0x0939_d927_53cc_5dc8, 24 | 0xef77_e7d7_3676_6c5d, 25 | 0x2bf0_3e1a_29aa_871f, 26 | 0x150c_93fe_f652_fb1c, 27 | ]), 28 | ], 29 | [ 30 | vesta::Base::from_raw([ 31 | 0x1425_9dce_5377_82b2, 32 | 0x03cc_0a60_141e_894e, 33 | 0x955d_55db_56dc_57c1, 34 | 0x3270_661e_6892_8b3a, 35 | ]), 36 | vesta::Base::from_raw([ 37 | 0xce9f_b9ff_c345_afb3, 38 | 0xb407_c370_f2b5_a1cc, 39 | 0xa0b7_afe4_e205_7299, 40 | 0x073f_116f_0412_2e25, 41 | ]), 42 | vesta::Base::from_raw([ 43 | 0x8eba_d76f_c715_54d8, 44 | 0x55c9_cd20_61ae_93ca, 45 | 0x7aff_d09c_1f53_f5fd, 46 | 0x2a32_ec5c_4ee5_b183, 47 | ]), 48 | ], 49 | [ 50 | vesta::Base::from_raw([ 51 | 0x2d8c_cbe2_92ef_eead, 52 | 0x634d_24fc_6e25_59f2, 53 | 0x651e_2cfc_7406_28ca, 54 | 0x2703_26ee_039d_f19e, 55 | ]), 56 | vesta::Base::from_raw([ 57 | 0xa068_fc37_c182_e274, 58 | 0x8af8_95bc_e012_f182, 59 | 0xdc10_0fe7_fcfa_5491, 60 | 0x27c6_642a_c633_bc66, 61 | ]), 62 | vesta::Base::from_raw([ 63 | 0x9ca1_8682_e26d_7ff9, 64 | 0x710e_1fb6_ab97_6a45, 65 | 0xd27f_5739_6989_129d, 66 | 0x1bdf_d8b0_1401_c70a, 67 | ]), 68 | ], 69 | [ 70 | vesta::Base::from_raw([ 71 | 0xc832_d824_261a_35ea, 72 | 0xf4f6_fb3f_9054_d373, 73 | 0x14b9_d6a9_c84d_d678, 74 | 0x162a_14c6_2f9a_89b8, 75 | ]), 76 | vesta::Base::from_raw([ 77 | 0xf798_2466_7b5b_6bec, 78 | 0xac0a_1fc7_1e2c_f0c0, 79 | 0x2af6_f79e_3127_feea, 80 | 0x2d19_3e0f_76de_586b, 81 | ]), 82 | vesta::Base::from_raw([ 83 | 0x5d0b_f58d_c8a4_aa94, 84 | 0x4fef_f829_8499_0ff8, 85 | 0x8169_6ef1_104e_674f, 86 | 0x044c_a3cc_4a85_d73b, 87 | ]), 88 | ], 89 | [ 90 | vesta::Base::from_raw([ 91 | 0x6198_785f_0cd6_b9af, 92 | 0xb8d9_e2d4_f314_f46f, 93 | 0x1d04_5341_6d3e_235c, 94 | 0x1cba_f2b3_71da_c6a8, 95 | ]), 96 | vesta::Base::from_raw([ 97 | 0x343e_0761_0f3f_ede5, 98 | 0x293c_4ab0_38fd_bbdc, 99 | 0x0e6c_49d0_61b6_b5f4, 100 | 0x1d5b_2777_692c_205b, 101 | ]), 102 | vesta::Base::from_raw([ 103 | 0xf60e_971b_8d73_b04f, 104 | 0x06a9_adb0_c1e6_f962, 105 | 0xaa30_535b_dd74_9a7e, 106 | 0x2e9b_dbba_3dd3_4bff, 107 | ]), 108 | ], 109 | [ 110 | vesta::Base::from_raw([ 111 | 0x035a_1366_1f22_418b, 112 | 0xde40_fbe2_6d04_7b05, 113 | 0x8bd5_bae3_6969_299f, 114 | 0x2de1_1886_b180_11ca, 115 | ]), 116 | vesta::Base::from_raw([ 117 | 0xbc99_8884_ba96_a721, 118 | 0x2ab9_395c_449b_e947, 119 | 0x0d5b_4a3f_1841_dcd8, 120 | 0x2e07_de17_80b8_a70d, 121 | ]), 122 | vesta::Base::from_raw([ 123 | 0x825e_4c2b_b749_25ca, 124 | 0x2504_40a9_9d6b_8af3, 125 | 0xbbdb_63db_d52d_ad16, 126 | 0x0f69_f185_4d20_ca0c, 127 | ]), 128 | ], 129 | [ 130 | vesta::Base::from_raw([ 131 | 0x816c_0594_22dc_705e, 132 | 0x6ce5_1135_07f9_6de9, 133 | 0x0d13_5dc6_39fb_09a4, 134 | 0x2eb1_b254_17fe_1767, 135 | ]), 136 | vesta::Base::from_raw([ 137 | 0xb8b1_bdf4_953b_d82c, 138 | 0xff36_c661_d26c_c42d, 139 | 0x8c24_cb44_c3fa_b48a, 140 | 0x115c_d0a0_643c_fb98, 141 | ]), 142 | vesta::Base::from_raw([ 143 | 0xde80_1612_311d_04cd, 144 | 0xbb57_ddf1_4e0f_958a, 145 | 0x066d_7378_b999_868b, 146 | 0x26ca_293f_7b2c_462d, 147 | ]), 148 | ], 149 | [ 150 | vesta::Base::from_raw([ 151 | 0xf520_9d14_b248_20ca, 152 | 0x0f16_0bf9_f71e_967f, 153 | 0x2a83_0aa1_6241_2cd9, 154 | 0x17bf_1b93_c4c7_e01a, 155 | ]), 156 | vesta::Base::from_raw([ 157 | 0x05c8_6f2e_7dc2_93c5, 158 | 0xe03c_0354_bd8c_fd38, 159 | 0xa24f_8456_369c_85df, 160 | 0x35b4_1a7a_c4f3_c571, 161 | ]), 162 | vesta::Base::from_raw([ 163 | 0x72ac_156a_f435_d09e, 164 | 0x64e1_4d3b_eb2d_ddde, 165 | 0x4359_2799_4849_bea9, 166 | 0x3b14_8008_0523_c439, 167 | ]), 168 | ], 169 | [ 170 | vesta::Base::from_raw([ 171 | 0x2716_18d8_74b1_4c6d, 172 | 0x08e2_8644_2a2d_3eb2, 173 | 0x4950_856d_c907_d575, 174 | 0x2cc6_8100_31dc_1b0d, 175 | ]), 176 | vesta::Base::from_raw([ 177 | 0x91f3_18c0_9f0c_b566, 178 | 0x9e51_7aa9_3b78_341d, 179 | 0x0596_18e2_afd2_ef99, 180 | 0x25bd_bbed_a1bd_e8c1, 181 | ]), 182 | vesta::Base::from_raw([ 183 | 0xc631_3487_073f_7f7b, 184 | 0x2a5e_d0a2_7b61_926c, 185 | 0xb95f_33c2_5dde_8ac0, 186 | 0x392a_4a87_58e0_6ee8, 187 | ]), 188 | ], 189 | [ 190 | vesta::Base::from_raw([ 191 | 0xe7bb_cef0_2eb5_866c, 192 | 0x5e6a_6fd1_5db8_9365, 193 | 0x9aa6_111f_4de0_0948, 194 | 0x272a_5587_8a08_442b, 195 | ]), 196 | vesta::Base::from_raw([ 197 | 0x9b92_5b3c_5b21_e0e2, 198 | 0xa6eb_ba01_1694_dd12, 199 | 0xefa1_3c4e_60e2_6239, 200 | 0x2d5b_308b_0cf0_2cdf, 201 | ]), 202 | vesta::Base::from_raw([ 203 | 0xef38_c57c_3116_73ac, 204 | 0x44df_f42f_18b4_6c56, 205 | 0xdd5d_293d_72e2_e5f2, 206 | 0x1654_9fc6_af2f_3b72, 207 | ]), 208 | ], 209 | [ 210 | vesta::Base::from_raw([ 211 | 0x9b71_26d9_b468_60df, 212 | 0x7639_8265_3442_0311, 213 | 0xfa69_c3a2_ad52_f76d, 214 | 0x1b10_bb7a_82af_ce39, 215 | ]), 216 | vesta::Base::from_raw([ 217 | 0x90d2_7f6a_00b7_dfc8, 218 | 0xd1b3_6968_ba04_05c0, 219 | 0xc79c_2df7_dc98_a3be, 220 | 0x0f1e_7505_ebd9_1d2f, 221 | ]), 222 | vesta::Base::from_raw([ 223 | 0xff45_7756_b819_bb20, 224 | 0x797f_d6e3_f18e_b1ca, 225 | 0x537a_7497_a3b4_3f46, 226 | 0x2f31_3faf_0d3f_6187, 227 | ]), 228 | ], 229 | [ 230 | vesta::Base::from_raw([ 231 | 0xf0bc_3e73_2ecb_26f6, 232 | 0x5cad_11eb_f0f7_ceb8, 233 | 0xfa3c_a61c_0ed1_5bc5, 234 | 0x3a5c_bb6d_e450_b481, 235 | ]), 236 | vesta::Base::from_raw([ 237 | 0x8655_27cb_ca91_5982, 238 | 0x51ba_a6e2_0f89_2b62, 239 | 0xd920_86e2_53b4_39d6, 240 | 0x3dab_54bc_9bef_688d, 241 | ]), 242 | vesta::Base::from_raw([ 243 | 0x3680_45ac_f2b7_1ae3, 244 | 0x4c24_b33b_410f_efd4, 245 | 0xe280_d316_7012_3f74, 246 | 0x06db_fb42_b979_884d, 247 | ]), 248 | ], 249 | [ 250 | vesta::Base::from_raw([ 251 | 0xa7fc_32d2_2f18_b9d3, 252 | 0xb8d2_de72_e3d2_c9ec, 253 | 0xc6f0_39ea_1973_a63e, 254 | 0x068d_6b46_08aa_e810, 255 | ]), 256 | vesta::Base::from_raw([ 257 | 0x2b5d_fcc5_5725_55df, 258 | 0xb868_a7d7_e1f1_f69a, 259 | 0x0ee2_58c9_b8fd_fccd, 260 | 0x366e_bfaf_a3ad_381c, 261 | ]), 262 | vesta::Base::from_raw([ 263 | 0xe6bc_229e_95bc_76b1, 264 | 0x7ef6_6d89_d044_d022, 265 | 0x04db_3024_f41d_3f56, 266 | 0x3967_8f65_512f_1ee4, 267 | ]), 268 | ], 269 | [ 270 | vesta::Base::from_raw([ 271 | 0xe534_c88f_e53d_85fe, 272 | 0xcf82_c25f_99dc_01a4, 273 | 0xd58b_7750_a3bc_2fe1, 274 | 0x2166_8f01_6a80_63c0, 275 | ]), 276 | vesta::Base::from_raw([ 277 | 0x4bef_429b_c533_1608, 278 | 0xe34d_ea56_439f_e195, 279 | 0x1bc7_4936_3e98_a768, 280 | 0x39d0_0994_a8a5_046a, 281 | ]), 282 | vesta::Base::from_raw([ 283 | 0x770c_956f_60d8_81b3, 284 | 0xb163_d416_05d3_9f99, 285 | 0x6b20_3bbe_12fb_3425, 286 | 0x1f9d_bdc3_f843_1263, 287 | ]), 288 | ], 289 | [ 290 | vesta::Base::from_raw([ 291 | 0x9794_a9f7_c336_eab2, 292 | 0xbe0b_c829_fe5e_66c6, 293 | 0xe5f1_7b9e_0ee0_cab6, 294 | 0x0277_45a9_cddf_ad95, 295 | ]), 296 | vesta::Base::from_raw([ 297 | 0x5202_5657_abd8_aee0, 298 | 0x2fa4_3fe2_0a45_c78d, 299 | 0x788d_695c_61e9_3212, 300 | 0x1cec_0803_c504_b635, 301 | ]), 302 | vesta::Base::from_raw([ 303 | 0xd387_2a95_59a0_3a73, 304 | 0xed50_82c8_dbf3_1365, 305 | 0x7207_7448_ef87_cc6e, 306 | 0x1235_23d7_5e9f_abc1, 307 | ]), 308 | ], 309 | [ 310 | vesta::Base::from_raw([ 311 | 0x0017_79e3_a1d3_57f4, 312 | 0x27fe_ba35_975e_e7e5, 313 | 0xf419_b848_e5d6_94bf, 314 | 0x1723_d145_2c9c_f02d, 315 | ]), 316 | vesta::Base::from_raw([ 317 | 0x9dab_1ee4_dcf9_6622, 318 | 0x21c3_f776_f572_836d, 319 | 0xfcc0_573d_7e61_3694, 320 | 0x1739_d180_a160_10bd, 321 | ]), 322 | vesta::Base::from_raw([ 323 | 0x7029_0452_042d_048d, 324 | 0xfafa_96fb_eb0a_b893, 325 | 0xacce_3239_1794_b627, 326 | 0x2d4e_6354_da9c_c554, 327 | ]), 328 | ], 329 | [ 330 | vesta::Base::from_raw([ 331 | 0x670b_cf6f_8b48_5dcd, 332 | 0x8f3b_d43f_9926_0621, 333 | 0x4a86_9553_c9d0_07f8, 334 | 0x153e_e614_2e53_5e33, 335 | ]), 336 | vesta::Base::from_raw([ 337 | 0xd258_d2e2_b778_2172, 338 | 0x968a_d442_4af8_3700, 339 | 0x635e_f7e7_a430_b486, 340 | 0x0c45_bfd3_a69a_aa65, 341 | ]), 342 | vesta::Base::from_raw([ 343 | 0x0e56_33d2_51f7_3307, 344 | 0x6897_ac0a_8ffa_5ff1, 345 | 0xf2d5_6aec_8314_4600, 346 | 0x0adf_d53b_256a_6957, 347 | ]), 348 | ], 349 | [ 350 | vesta::Base::from_raw([ 351 | 0xac9d_36a8_b751_6d63, 352 | 0x3f87_b28f_1c1b_e4bd, 353 | 0x8cd1_726b_7cba_b8ee, 354 | 0x315d_2ac8_ebdb_ac3c, 355 | ]), 356 | vesta::Base::from_raw([ 357 | 0x299c_e44e_a423_d8e1, 358 | 0xc9bb_60d1_f695_9879, 359 | 0xcfae_c23d_2b16_883f, 360 | 0x1b84_7271_2d02_eef4, 361 | ]), 362 | vesta::Base::from_raw([ 363 | 0xc4a5_4041_98ad_f70c, 364 | 0x367d_2c54_e369_28c9, 365 | 0xbd0b_70fa_2255_eb6f, 366 | 0x3c1c_d07e_fda6_ff24, 367 | ]), 368 | ], 369 | [ 370 | vesta::Base::from_raw([ 371 | 0xbbe5_23ae_f9ab_107a, 372 | 0x4a16_073f_738f_7e0c, 373 | 0x687f_4e51_b2e1_dcd3, 374 | 0x1360_52d2_6bb3_d373, 375 | ]), 376 | vesta::Base::from_raw([ 377 | 0x676c_36c2_4ef9_67dd, 378 | 0x7b3c_fbb8_7303_2681, 379 | 0xc1bd_d859_a123_2a1d, 380 | 0x16c9_6bee_f6a0_a848, 381 | ]), 382 | vesta::Base::from_raw([ 383 | 0x067e_ec7f_2d63_40c4, 384 | 0x0123_87ba_b4f1_662d, 385 | 0x2ab7_fed8_f499_a9fb, 386 | 0x284b_38c5_7ff6_5c26, 387 | ]), 388 | ], 389 | [ 390 | vesta::Base::from_raw([ 391 | 0xaf1d_ff20_4c92_2f86, 392 | 0xfc06_772c_1c04_11a6, 393 | 0x39e2_4219_8897_d17c, 394 | 0x0c59_93d1_75e8_1f66, 395 | ]), 396 | vesta::Base::from_raw([ 397 | 0xbbf5_3f67_b1f8_7b15, 398 | 0xf248_87ad_48e1_7759, 399 | 0xfcda_655d_1ba9_c8f9, 400 | 0x03bf_7a3f_7bd0_43da, 401 | ]), 402 | vesta::Base::from_raw([ 403 | 0x9b5c_d09e_36d8_be62, 404 | 0x4c8f_9cbe_69f0_e827, 405 | 0xb0cf_9995_67f0_0e73, 406 | 0x3188_fe4e_e9f9_fafb, 407 | ]), 408 | ], 409 | [ 410 | vesta::Base::from_raw([ 411 | 0xafea_99a2_ec6c_595a, 412 | 0x3af5_bf77_c1c4_2652, 413 | 0x5a39_768c_480d_61e1, 414 | 0x171f_528c_cf65_8437, 415 | ]), 416 | vesta::Base::from_raw([ 417 | 0x5a05_63b9_b8e9_f1d5, 418 | 0x812c_3286_ee70_0067, 419 | 0x196e_4185_9b35_ef88, 420 | 0x12f4_175c_4ab4_5afc, 421 | ]), 422 | vesta::Base::from_raw([ 423 | 0x0e74_d4d3_6911_8b79, 424 | 0x7e23_e1aa_be96_cfab, 425 | 0x8f8f_dcf8_00a9_ac69, 426 | 0x3a50_9e15_5cb7_ebfd, 427 | ]), 428 | ], 429 | [ 430 | vesta::Base::from_raw([ 431 | 0x9871_2c65_678c_fd30, 432 | 0x984b_c8f2_e4c1_b69e, 433 | 0x1a89_920e_2504_c3b3, 434 | 0x10f2_a685_df4a_27c8, 435 | ]), 436 | vesta::Base::from_raw([ 437 | 0xe8a1_6728_cc9d_4918, 438 | 0x5457_3c93_33c5_6321, 439 | 0x1d8d_93d5_4ab9_1a0e, 440 | 0x09e5_f497_90c8_a0e2, 441 | ]), 442 | vesta::Base::from_raw([ 443 | 0x609a_7403_47cf_5fea, 444 | 0x42d1_7ed6_ee0f_ab7e, 445 | 0x2bf3_5705_d9f8_4a34, 446 | 0x352d_69be_d80e_e3e5, 447 | ]), 448 | ], 449 | [ 450 | vesta::Base::from_raw([ 451 | 0x3a75_8af6_fa84_e0e8, 452 | 0xc634_debd_281b_76a6, 453 | 0x4915_62fa_f2b1_90d3, 454 | 0x058e_e73b_a9f3_f293, 455 | ]), 456 | vesta::Base::from_raw([ 457 | 0x621a_1325_10a4_3904, 458 | 0x092c_b921_19bc_76be, 459 | 0xcd0f_1fc5_5b1a_3250, 460 | 0x232f_99cc_911e_ddd9, 461 | ]), 462 | vesta::Base::from_raw([ 463 | 0xc3b9_7c1e_301b_c213, 464 | 0xf9ef_d52c_a6bc_2961, 465 | 0x86c2_2c6c_5d48_69f0, 466 | 0x201b_eed7_b8f3_ab81, 467 | ]), 468 | ], 469 | [ 470 | vesta::Base::from_raw([ 471 | 0xbf6b_3431_ba94_e9bc, 472 | 0x2938_8842_744a_1210, 473 | 0xa1c9_291d_5860_2f51, 474 | 0x1376_dce6_5800_30c6, 475 | ]), 476 | vesta::Base::from_raw([ 477 | 0x6454_843c_5486_d7b3, 478 | 0x072b_a8b0_2d92_e722, 479 | 0x2b33_56c3_8238_f761, 480 | 0x1793_199e_6fd6_ba34, 481 | ]), 482 | vesta::Base::from_raw([ 483 | 0x06a3_f1d3_b433_311b, 484 | 0x3c66_160d_c62a_acac, 485 | 0x9fee_9c20_c87a_67df, 486 | 0x22de_7a74_88dc_c735, 487 | ]), 488 | ], 489 | [ 490 | vesta::Base::from_raw([ 491 | 0x30d6_e3fd_516b_47a8, 492 | 0xdbe0_b77f_ae77_e1d0, 493 | 0xdf8f_f37f_e2d8_edf8, 494 | 0x3514_d5e9_066b_b160, 495 | ]), 496 | vesta::Base::from_raw([ 497 | 0x1937_7427_137a_81c7, 498 | 0xff45_3d6f_900f_144a, 499 | 0xf919_a00d_abbf_5fa5, 500 | 0x30cd_3006_931a_d636, 501 | ]), 502 | vesta::Base::from_raw([ 503 | 0x5b6a_7422_0692_b506, 504 | 0x8f9e_4b2c_ae2e_bb51, 505 | 0x41f8_1a5c_f613_c8df, 506 | 0x253d_1a5c_5293_4127, 507 | ]), 508 | ], 509 | [ 510 | vesta::Base::from_raw([ 511 | 0x73f6_66cb_86a4_8e8e, 512 | 0x851b_3a59_c990_fafc, 513 | 0xa35e_9613_e7f5_fe92, 514 | 0x035b_461c_02d7_9d19, 515 | ]), 516 | vesta::Base::from_raw([ 517 | 0x7cfb_f86a_3aa0_4780, 518 | 0x92b1_283c_2d5f_ccde, 519 | 0x5bc0_0eed_d56b_93e0, 520 | 0x23a9_9280_79d1_75bd, 521 | ]), 522 | vesta::Base::from_raw([ 523 | 0xf1e4_ccd7_3fa0_0a82, 524 | 0xb5e2_ea34_36ee_f957, 525 | 0xf159_4a07_63c6_11ab, 526 | 0x13a7_785a_e134_ea92, 527 | ]), 528 | ], 529 | [ 530 | vesta::Base::from_raw([ 531 | 0xbbf0_4f52_52de_4279, 532 | 0x3889_c578_6344_6d88, 533 | 0x4962_ae3c_0da1_7e31, 534 | 0x39fc_e308_b7d4_3c57, 535 | ]), 536 | vesta::Base::from_raw([ 537 | 0x3b57_e344_89b5_3fad, 538 | 0xbef0_0a08_c6ed_38d2, 539 | 0xc0fd_f016_62f6_0d22, 540 | 0x1aae_1883_3f8e_1d3a, 541 | ]), 542 | vesta::Base::from_raw([ 543 | 0x5551_3e03_3398_513f, 544 | 0x27c1_b3fd_8f85_d8a8, 545 | 0x8b2e_80c0_64fd_83ed, 546 | 0x1a76_1ce8_2400_af01, 547 | ]), 548 | ], 549 | [ 550 | vesta::Base::from_raw([ 551 | 0x5244_ca74_9b73_e481, 552 | 0xdcf6_af28_30a5_0287, 553 | 0x16dd_1a87_ca22_e1cc, 554 | 0x275a_03e4_5add_a7c3, 555 | ]), 556 | vesta::Base::from_raw([ 557 | 0x58a2_53cf_b6a9_5786, 558 | 0x07e5_6145_3fc5_648b, 559 | 0xeb08_e47e_5fea_bcf8, 560 | 0x2e5a_10f0_8b5a_b8bb, 561 | ]), 562 | vesta::Base::from_raw([ 563 | 0xe033_d82c_efe7_8ce3, 564 | 0xc141_a5b6_d594_bec4, 565 | 0xb84e_9c33_3b29_32f1, 566 | 0x1459_cb85_8720_8473, 567 | ]), 568 | ], 569 | [ 570 | vesta::Base::from_raw([ 571 | 0x5cec_7e7b_338f_be1b, 572 | 0x52f9_332f_bffc_fbbd, 573 | 0x7b92_ce81_0e14_a400, 574 | 0x193a_e592_1d78_b5de, 575 | ]), 576 | vesta::Base::from_raw([ 577 | 0x6022_4be6_7248_e82c, 578 | 0x3743_84f4_a072_8205, 579 | 0x8911_1fb2_c466_0281, 580 | 0x3097_898a_5d00_11a4, 581 | ]), 582 | vesta::Base::from_raw([ 583 | 0x5499_80de_8629_30f5, 584 | 0x1979_b2d1_c465_b4d9, 585 | 0x5717_82fd_96ce_54b4, 586 | 0x378d_97bf_8c86_4ae7, 587 | ]), 588 | ], 589 | [ 590 | vesta::Base::from_raw([ 591 | 0x37ea_32a9_71d1_7884, 592 | 0xdbc7_f5cb_4609_3421, 593 | 0x8813_6287_ce37_6b08, 594 | 0x2eb0_4ea7_c01d_97ec, 595 | ]), 596 | vesta::Base::from_raw([ 597 | 0xead3_726f_1af2_e7b0, 598 | 0x861c_bda4_7680_4e6c, 599 | 0x2302_a1c2_2e49_baec, 600 | 0x3642_5347_ea03_f641, 601 | ]), 602 | vesta::Base::from_raw([ 603 | 0xecd6_27e5_9590_d09e, 604 | 0x3f5b_5ca5_a19a_9701, 605 | 0xcc99_6cd8_5c98_a1d8, 606 | 0x26b7_2df4_7408_ad42, 607 | ]), 608 | ], 609 | [ 610 | vesta::Base::from_raw([ 611 | 0x59be_ce31_f0a3_1e95, 612 | 0xde01_212e_e458_8f89, 613 | 0x1f05_636c_610b_89aa, 614 | 0x1301_80e4_4e29_24db, 615 | ]), 616 | vesta::Base::from_raw([ 617 | 0x9ea8_e7bc_7926_3550, 618 | 0xdf77_93cc_89e5_b52f, 619 | 0x7327_5aca_ed5f_579c, 620 | 0x219e_9773_7d39_79ba, 621 | ]), 622 | vesta::Base::from_raw([ 623 | 0x9c12_635d_f251_d153, 624 | 0x3b06_72dd_7d42_cbb4, 625 | 0x3461_363f_81c4_89a2, 626 | 0x3cdb_9359_8a5c_a528, 627 | ]), 628 | ], 629 | [ 630 | vesta::Base::from_raw([ 631 | 0x2861_ce16_f219_d5a9, 632 | 0x4ad0_4470_45a7_c5aa, 633 | 0x2072_4b92_7a0c_a81c, 634 | 0x0e59_e6f3_32d7_ed37, 635 | ]), 636 | vesta::Base::from_raw([ 637 | 0x43b0_a3fc_ff20_36bd, 638 | 0x172c_c07b_9d33_fbf9, 639 | 0x3d73_6946_7222_697a, 640 | 0x1b06_4342_d51a_4275, 641 | ]), 642 | vesta::Base::from_raw([ 643 | 0x3eb3_1022_8a0e_5f6c, 644 | 0x78fa_9fb9_1712_21b7, 645 | 0x2f36_3c55_b288_2e0b, 646 | 0x30b8_2a99_8cbd_8e8a, 647 | ]), 648 | ], 649 | [ 650 | vesta::Base::from_raw([ 651 | 0xe46f_6d42_9874_0107, 652 | 0x8ad7_1ea7_15be_0573, 653 | 0x63df_7a76_e858_a4aa, 654 | 0x23e4_ab37_183a_cba4, 655 | ]), 656 | vesta::Base::from_raw([ 657 | 0xfca9_95e2_b599_14a1, 658 | 0xacfe_1464_0de0_44f2, 659 | 0x5d33_094e_0bed_a75b, 660 | 0x2795_d5c5_fa42_8022, 661 | ]), 662 | vesta::Base::from_raw([ 663 | 0xc26d_909d_ee8b_53c0, 664 | 0xa668_7c3d_f16c_8fe4, 665 | 0xd765_f26d_d03f_4c45, 666 | 0x3001_ca40_1e89_601c, 667 | ]), 668 | ], 669 | [ 670 | vesta::Base::from_raw([ 671 | 0xe7fe_a6bd_f347_1380, 672 | 0xe84b_5beb_ae4e_501d, 673 | 0xf7bf_86e8_9280_827f, 674 | 0x0072_e45c_c676_b08e, 675 | ]), 676 | vesta::Base::from_raw([ 677 | 0xd0c5_4dde_b26b_86c0, 678 | 0xb648_29e2_d40e_41bd, 679 | 0xe2ab_e4c5_18ce_599e, 680 | 0x13de_7054_8487_4bb5, 681 | ]), 682 | vesta::Base::from_raw([ 683 | 0x3891_5b43_2a99_59a5, 684 | 0x82bb_18e5_af1b_05bb, 685 | 0x3159_50f1_211d_efe8, 686 | 0x0408_a9fc_f9d6_1abf, 687 | ]), 688 | ], 689 | [ 690 | vesta::Base::from_raw([ 691 | 0x3407_0cbe_e268_86a0, 692 | 0xae4d_23b0_b41b_e9a8, 693 | 0xbb4e_4a14_00cc_d2c4, 694 | 0x2780_b9e7_5b55_676e, 695 | ]), 696 | vesta::Base::from_raw([ 697 | 0x9405_5920_98b4_056f, 698 | 0xdc4d_8fbe_fe24_405a, 699 | 0xf803_33ec_8563_4ac9, 700 | 0x3a57_0d4d_7c4e_7ac3, 701 | ]), 702 | vesta::Base::from_raw([ 703 | 0x78d2_b247_8995_20b4, 704 | 0xe2cc_1507_bebd_cc62, 705 | 0xf347_c247_fcf0_9294, 706 | 0x0c13_cca7_cb1f_9d2c, 707 | ]), 708 | ], 709 | [ 710 | vesta::Base::from_raw([ 711 | 0x2e8c_88f7_7074_70e0, 712 | 0x0b50_bb2e_b82d_f74d, 713 | 0xd261_4a19_7c6b_794b, 714 | 0x14f5_9baa_03cd_0ca4, 715 | ]), 716 | vesta::Base::from_raw([ 717 | 0xbe52_476e_0a16_f3be, 718 | 0xa51d_54ed_e661_67f5, 719 | 0x6f54_6e17_04c3_9c60, 720 | 0x307d_efee_925d_fb43, 721 | ]), 722 | vesta::Base::from_raw([ 723 | 0x380b_67d8_0473_dce3, 724 | 0x6611_0683_6adf_e5e7, 725 | 0x7a07_e767_4b5a_2621, 726 | 0x1960_cd51_1a91_e060, 727 | ]), 728 | ], 729 | [ 730 | vesta::Base::from_raw([ 731 | 0x15aa_f1f7_7125_89dd, 732 | 0xb8ee_335d_8828_4cbe, 733 | 0xca2a_d0fb_5667_2500, 734 | 0x2301_ef9c_63ea_84c5, 735 | ]), 736 | vesta::Base::from_raw([ 737 | 0x5e68_478c_4d60_27a9, 738 | 0xc861_82d1_b424_6b58, 739 | 0xd10f_4cd5_2be9_7f6b, 740 | 0x029a_5a47_da79_a488, 741 | ]), 742 | vesta::Base::from_raw([ 743 | 0x2cc4_f962_eaae_2260, 744 | 0xf97f_e46b_6a92_5428, 745 | 0x2360_d17d_890e_55cb, 746 | 0x32d7_b16a_7f11_cc96, 747 | ]), 748 | ], 749 | [ 750 | vesta::Base::from_raw([ 751 | 0xc0ca_b915_d536_3d9f, 752 | 0xa5f2_404c_d7b3_5eb0, 753 | 0x18e8_57a9_8d49_8cf7, 754 | 0x2670_3e48_c03b_81ca, 755 | ]), 756 | vesta::Base::from_raw([ 757 | 0xf691_123a_e112_b928, 758 | 0xf443_88bd_6b89_221e, 759 | 0x88ac_8d25_a246_03f1, 760 | 0x0486_82a3_5b32_65bc, 761 | ]), 762 | vesta::Base::from_raw([ 763 | 0x3ab7_defc_b8d8_03e2, 764 | 0x91d6_e171_5164_775e, 765 | 0xd72c_ddc6_cf06_b507, 766 | 0x06b1_3904_41fa_7030, 767 | ]), 768 | ], 769 | [ 770 | vesta::Base::from_raw([ 771 | 0xbcd7_9541_4a6e_2e86, 772 | 0x43b3_60f6_386a_86d7, 773 | 0x1689_426d_ce05_fcd8, 774 | 0x31aa_0eeb_868c_626d, 775 | ]), 776 | vesta::Base::from_raw([ 777 | 0xed77_f5d5_76b9_9cc3, 778 | 0x90ef_d8f4_1b20_78b2, 779 | 0x057a_bad3_764c_104b, 780 | 0x2394_64f7_5bf7_b6af, 781 | ]), 782 | vesta::Base::from_raw([ 783 | 0xb2cb_4873_07c1_cecf, 784 | 0xa5cc_47c5_9654_b2a7, 785 | 0xa45e_19ed_813a_54ab, 786 | 0x0a64_d4c0_4fd4_26bd, 787 | ]), 788 | ], 789 | [ 790 | vesta::Base::from_raw([ 791 | 0x1f73_1532_2f65_8735, 792 | 0x777c_7a92_1a06_2e9d, 793 | 0x576a_4ad2_5986_0fb1, 794 | 0x21fb_bdbb_7367_0734, 795 | ]), 796 | vesta::Base::from_raw([ 797 | 0x6743_2400_3fc5_2146, 798 | 0x5b86_d294_63d3_1564, 799 | 0xd937_1ca2_eb95_acf3, 800 | 0x31b8_6f3c_f017_05d4, 801 | ]), 802 | vesta::Base::from_raw([ 803 | 0x7045_f48a_a4eb_4f6f, 804 | 0x1354_1d65_157e_e1ce, 805 | 0x05ef_1736_d090_56f6, 806 | 0x2bfd_e533_5437_7c91, 807 | ]), 808 | ], 809 | [ 810 | vesta::Base::from_raw([ 811 | 0x5a13_a58d_2001_1e2f, 812 | 0xf4d5_239c_11d0_eafa, 813 | 0xd558_f36e_65f8_eca7, 814 | 0x1233_ca93_6ec2_4671, 815 | ]), 816 | vesta::Base::from_raw([ 817 | 0x6e70_af0a_7a92_4b3a, 818 | 0x8780_58d0_234a_576f, 819 | 0xc437_846d_8e0b_2b30, 820 | 0x27d4_52a4_3ac7_dea2, 821 | ]), 822 | vesta::Base::from_raw([ 823 | 0xa025_76b9_4392_f980, 824 | 0x6a30_641a_1c3d_87b2, 825 | 0xe816_ea8d_a493_e0fa, 826 | 0x2699_dba8_2184_e413, 827 | ]), 828 | ], 829 | [ 830 | vesta::Base::from_raw([ 831 | 0x608c_6f7a_61b5_6e55, 832 | 0xf185_8466_4f8c_ab49, 833 | 0xc398_8bae_e42e_4b10, 834 | 0x36c7_22f0_efcc_8803, 835 | ]), 836 | vesta::Base::from_raw([ 837 | 0x6e49_ac17_0dbb_7fcd, 838 | 0x85c3_8899_a7b5_a833, 839 | 0x08b0_f2ec_89cc_aa37, 840 | 0x02b3_ff48_861e_339b, 841 | ]), 842 | vesta::Base::from_raw([ 843 | 0xa8c5_ae03_ad98_e405, 844 | 0x6fc3_ff4c_49eb_59ad, 845 | 0x6016_2f44_27bc_657b, 846 | 0x0b70_d061_d58d_8a7f, 847 | ]), 848 | ], 849 | [ 850 | vesta::Base::from_raw([ 851 | 0x2e06_cc4a_f33b_0a06, 852 | 0xad3d_e8be_46ed_9693, 853 | 0xf875_3ade_b9d7_cee2, 854 | 0x3fc2_a13f_127f_96a4, 855 | ]), 856 | vesta::Base::from_raw([ 857 | 0xc120_80ac_117e_e15f, 858 | 0x00cb_3d62_1e17_1d80, 859 | 0x1bd6_3434_ac8c_419f, 860 | 0x0c41_a6e4_8dd2_3a51, 861 | ]), 862 | vesta::Base::from_raw([ 863 | 0x9685_213e_9692_f5e1, 864 | 0x72aa_ad7e_4e75_339d, 865 | 0xed44_7653_7169_084e, 866 | 0x2de8_072a_6bd8_6884, 867 | ]), 868 | ], 869 | [ 870 | vesta::Base::from_raw([ 871 | 0x0ad0_1184_567b_027c, 872 | 0xb81c_f735_cc9c_39c0, 873 | 0x9d34_96a3_d9fe_05ec, 874 | 0x0355_7a8f_7b38_a17f, 875 | ]), 876 | vesta::Base::from_raw([ 877 | 0x45bc_b5ac_0082_6abc, 878 | 0x060f_4336_3d81_8e54, 879 | 0xee97_6d34_282f_1a37, 880 | 0x0b5f_5955_2f49_8735, 881 | ]), 882 | vesta::Base::from_raw([ 883 | 0x2f29_09e1_7e22_b0df, 884 | 0xf5d6_46e5_7507_e548, 885 | 0xfedb_b185_70dc_7300, 886 | 0x0e29_23a5_fee7_b878, 887 | ]), 888 | ], 889 | [ 890 | vesta::Base::from_raw([ 891 | 0xf71e_ed73_f15b_3326, 892 | 0xcf1c_b37c_3b03_2af6, 893 | 0xc787_be97_020a_7fdd, 894 | 0x1d78_5005_a7a0_0592, 895 | ]), 896 | vesta::Base::from_raw([ 897 | 0x0acf_bfb2_23f8_f00d, 898 | 0xa590_b88a_3b06_0294, 899 | 0x0ba5_fedc_b8f2_5bd2, 900 | 0x1ad7_72c2_73d9_c6df, 901 | ]), 902 | vesta::Base::from_raw([ 903 | 0xc1ce_13d6_0f2f_5031, 904 | 0x8105_10eb_61f0_672d, 905 | 0xa78f_3275_c278_234b, 906 | 0x027b_d647_85fc_bd2a, 907 | ]), 908 | ], 909 | [ 910 | vesta::Base::from_raw([ 911 | 0x8337_f5e0_7923_a853, 912 | 0xe224_3134_6945_7b8e, 913 | 0xce6f_8ffe_a103_1b6d, 914 | 0x2080_0f44_1b4a_0526, 915 | ]), 916 | vesta::Base::from_raw([ 917 | 0xa33d_7bed_89a4_408a, 918 | 0x36cd_c8ee_d662_ad37, 919 | 0x6eea_2cd4_9f43_12b4, 920 | 0x3d5a_d61d_7b65_f938, 921 | ]), 922 | vesta::Base::from_raw([ 923 | 0x3bbb_ae94_cc19_5284, 924 | 0x1df9_6cc0_3ea4_b26d, 925 | 0x02c5_f91b_e4dd_8e3d, 926 | 0x1333_8bc3_51fc_46dd, 927 | ]), 928 | ], 929 | [ 930 | vesta::Base::from_raw([ 931 | 0xc527_1c29_7852_819e, 932 | 0x646c_49f9_b46c_bf19, 933 | 0xb87d_b1e2_af3e_a923, 934 | 0x25e5_2be5_07c9_2760, 935 | ]), 936 | vesta::Base::from_raw([ 937 | 0x5c38_0ab7_01b5_2ea9, 938 | 0xa34c_83a3_485c_6b2d, 939 | 0x7109_6d8b_1b98_3c98, 940 | 0x1c49_2d64_c157_aaa4, 941 | ]), 942 | vesta::Base::from_raw([ 943 | 0xa20c_0b3d_a0da_4ca3, 944 | 0xd434_87bc_288d_f682, 945 | 0xf4e6_c5e7_a573_f592, 946 | 0x0c5b_8015_7999_2718, 947 | ]), 948 | ], 949 | [ 950 | vesta::Base::from_raw([ 951 | 0x7ea3_3c93_e408_33cf, 952 | 0x584e_9e62_a7f9_554e, 953 | 0x6869_5c0c_d7cb_f43d, 954 | 0x1090_b1b4_d2be_be7a, 955 | ]), 956 | vesta::Base::from_raw([ 957 | 0xe383_e1ec_3baa_8d69, 958 | 0x1b21_8e35_ecf2_328e, 959 | 0x68f5_ce5c_bed1_9cad, 960 | 0x33e3_8018_a801_387a, 961 | ]), 962 | vesta::Base::from_raw([ 963 | 0xb76b_0b3d_787e_e953, 964 | 0x5f4a_02d2_8729_e3ae, 965 | 0xeef8_d83d_0e87_6bac, 966 | 0x1654_af18_772b_2da5, 967 | ]), 968 | ], 969 | [ 970 | vesta::Base::from_raw([ 971 | 0xef7c_e6a0_1326_5477, 972 | 0xbb08_9387_0367_ec6c, 973 | 0x4474_2de8_8c5a_b0d5, 974 | 0x1678_be3c_c9c6_7993, 975 | ]), 976 | vesta::Base::from_raw([ 977 | 0xaf5d_4789_3348_f766, 978 | 0xdaf1_8183_55b1_3b4f, 979 | 0x7ff9_c6be_546e_928a, 980 | 0x3780_bd1e_01f3_4c22, 981 | ]), 982 | vesta::Base::from_raw([ 983 | 0xa123_8032_0d7c_c1de, 984 | 0x5d11_e69a_a6c0_b98c, 985 | 0x0786_018e_7cb7_7267, 986 | 0x1e83_d631_5c9f_125b, 987 | ]), 988 | ], 989 | [ 990 | vesta::Base::from_raw([ 991 | 0x1799_603e_855c_e731, 992 | 0xc486_894d_76e0_c33b, 993 | 0x160b_4155_2f29_31c8, 994 | 0x354a_fd0a_2f9d_0b26, 995 | ]), 996 | vesta::Base::from_raw([ 997 | 0x8b99_7ee0_6be1_bff3, 998 | 0x60b0_0dbe_1fac_ed07, 999 | 0x2d8a_ffa6_2905_c5a5, 1000 | 0x00cd_6d29_f166_eadc, 1001 | ]), 1002 | vesta::Base::from_raw([ 1003 | 0x08d0_6419_1708_2f2c, 1004 | 0xc60d_0197_3f18_3057, 1005 | 0xdbe0_e3d7_cdbc_66ef, 1006 | 0x1d62_1935_2768_e3ae, 1007 | ]), 1008 | ], 1009 | [ 1010 | vesta::Base::from_raw([ 1011 | 0xfa08_dd98_0638_7577, 1012 | 0xafe3_ca1d_b8d4_f529, 1013 | 0xe48d_2370_d7d1_a142, 1014 | 0x1463_36e2_5db5_181d, 1015 | ]), 1016 | vesta::Base::from_raw([ 1017 | 0xa901_d3ce_84de_0ad4, 1018 | 0x022e_54b4_9c13_d907, 1019 | 0x997a_2116_3e2e_43df, 1020 | 0x0005_d8e0_85fd_72ee, 1021 | ]), 1022 | vesta::Base::from_raw([ 1023 | 0x1c36_f313_4196_4484, 1024 | 0x6f8e_bc1d_2296_021a, 1025 | 0x0dd5_e61c_8a4e_8642, 1026 | 0x364e_97c7_a389_3227, 1027 | ]), 1028 | ], 1029 | [ 1030 | vesta::Base::from_raw([ 1031 | 0xd7a0_0c03_d2e0_baaa, 1032 | 0xfa97_ec80_ad30_7a52, 1033 | 0x561c_6fff_1534_6878, 1034 | 0x0118_9910_671b_c16b, 1035 | ]), 1036 | vesta::Base::from_raw([ 1037 | 0x63fd_8ac5_7a95_ca8c, 1038 | 0x4c0f_7e00_1df4_90aa, 1039 | 0x5229_dfaa_0123_1a45, 1040 | 0x162a_7c80_f4d2_d12e, 1041 | ]), 1042 | vesta::Base::from_raw([ 1043 | 0x32e6_9efb_22f4_0b96, 1044 | 0xcaff_31b4_fda3_2124, 1045 | 0x2604_e4af_b09f_8603, 1046 | 0x2a0d_6c09_5766_66bb, 1047 | ]), 1048 | ], 1049 | [ 1050 | vesta::Base::from_raw([ 1051 | 0xc0a0_180f_8cbf_c0d2, 1052 | 0xf444_d10d_63a7_4e2c, 1053 | 0xe16a_4d60_3d5a_808e, 1054 | 0x0978_e5c5_1e1e_5649, 1055 | ]), 1056 | vesta::Base::from_raw([ 1057 | 0x03f4_460e_bc35_1b6e, 1058 | 0x0508_7d90_3bda_cfd1, 1059 | 0xebe1_9bbd_ce25_1011, 1060 | 0x1bdc_ee3a_aca9_cd25, 1061 | ]), 1062 | vesta::Base::from_raw([ 1063 | 0xf619_64bf_3ade_7670, 1064 | 0x0c94_7321_e007_5e3f, 1065 | 0xe494_7914_0b19_44fd, 1066 | 0x1862_cccb_70b5_b885, 1067 | ]), 1068 | ], 1069 | [ 1070 | vesta::Base::from_raw([ 1071 | 0xc326_7da6_e94a_dc50, 1072 | 0x39ee_99c1_cc6e_5dda, 1073 | 0xbc26_cc88_3a19_87e1, 1074 | 0x1f3e_91d8_63c1_6922, 1075 | ]), 1076 | vesta::Base::from_raw([ 1077 | 0x0f85_b4ac_2c36_7406, 1078 | 0xfa66_1465_c656_ad99, 1079 | 0xef5c_08f8_478f_663a, 1080 | 0x1af4_7a48_a601_6a49, 1081 | ]), 1082 | vesta::Base::from_raw([ 1083 | 0x0eab_cd87_e7d0_1b15, 1084 | 0x1c36_98b0_a2e3_da10, 1085 | 0x009d_5733_8c69_3505, 1086 | 0x3c8e_e901_956e_3d3f, 1087 | ]), 1088 | ], 1089 | [ 1090 | vesta::Base::from_raw([ 1091 | 0x8b94_7721_8967_3476, 1092 | 0xe10c_e2b7_069f_4dbd, 1093 | 0x68d0_b024_f591_b520, 1094 | 0x1660_a8cd_e7fe_c553, 1095 | ]), 1096 | vesta::Base::from_raw([ 1097 | 0x9d8d_0f67_fdaa_79d5, 1098 | 0x3963_c2c1_f558_6e2f, 1099 | 0x1303_9363_34dd_1132, 1100 | 0x0f6d_9919_29d5_e4e7, 1101 | ]), 1102 | vesta::Base::from_raw([ 1103 | 0x7a43_3091_e1ce_2d3a, 1104 | 0x4e7f_da77_0712_f343, 1105 | 0xcc62_5eaa_ab52_b4dc, 1106 | 0x02b9_cea1_921c_d9f6, 1107 | ]), 1108 | ], 1109 | [ 1110 | vesta::Base::from_raw([ 1111 | 0x3797_b2d8_3760_43b3, 1112 | 0xd8ca_f468_976f_0472, 1113 | 0x214f_7c67_84ac_b565, 1114 | 0x14a3_23b9_9b90_0331, 1115 | ]), 1116 | vesta::Base::from_raw([ 1117 | 0x347f_ef2c_00f0_953a, 1118 | 0x718b_7fbc_7788_af78, 1119 | 0xec01_ea79_642d_5760, 1120 | 0x1904_76b5_80cb_9277, 1121 | ]), 1122 | vesta::Base::from_raw([ 1123 | 0xff4e_7e6f_b268_dfd7, 1124 | 0x9660_902b_6008_7651, 1125 | 0xa424_63d3_0b44_2b6f, 1126 | 0x090a_3a9d_869d_2eef, 1127 | ]), 1128 | ], 1129 | [ 1130 | vesta::Base::from_raw([ 1131 | 0xf983_387e_a045_6203, 1132 | 0xe365_0013_04f9_a11e, 1133 | 0x0dbe_8fd2_270a_6795, 1134 | 0x3877_a955_8636_7567, 1135 | ]), 1136 | vesta::Base::from_raw([ 1137 | 0x39c0_af0f_e01f_4a06, 1138 | 0x6011_8c53_a218_1352, 1139 | 0x5df3_9a2c_c63d_dc0a, 1140 | 0x2d89_4691_240f_e953, 1141 | ]), 1142 | vesta::Base::from_raw([ 1143 | 0x1aca_9eaf_9bba_9850, 1144 | 0x5914_e855_eeb4_4aa1, 1145 | 0x7ef7_1780_2016_6189, 1146 | 0x21b9_c182_92bd_bc59, 1147 | ]), 1148 | ], 1149 | [ 1150 | vesta::Base::from_raw([ 1151 | 0x33f5_09a7_4ad9_d39b, 1152 | 0x272e_1cc6_c36a_2968, 1153 | 0x505a_05f2_a6ae_834c, 1154 | 0x2fe7_6be7_cff7_23e2, 1155 | ]), 1156 | vesta::Base::from_raw([ 1157 | 0x0df9_fa97_277f_a8b4, 1158 | 0xd15b_ff84_0dda_e8a5, 1159 | 0x9299_81d7_cfce_253b, 1160 | 0x187a_a448_f391_e3ca, 1161 | ]), 1162 | vesta::Base::from_raw([ 1163 | 0xf0c6_6af5_ffc7_3736, 1164 | 0x663c_cf7b_2ffe_4b5e, 1165 | 0x007a_b3aa_3617_f422, 1166 | 0x0b70_83ad_7517_07bf, 1167 | ]), 1168 | ], 1169 | [ 1170 | vesta::Base::from_raw([ 1171 | 0x2f9b_20f1_fbd4_9791, 1172 | 0x1975_b962_f6cb_8e0b, 1173 | 0x3bc4_ca99_02c5_2acb, 1174 | 0x030d_dbb4_7049_3f16, 1175 | ]), 1176 | vesta::Base::from_raw([ 1177 | 0x3a1c_62ca_8fbf_2525, 1178 | 0x8fb8_ab9d_60ea_17b2, 1179 | 0x950b_0ab1_8d35_46df, 1180 | 0x3130_fbaf_fb5a_a82a, 1181 | ]), 1182 | vesta::Base::from_raw([ 1183 | 0x43a8_7618_0dc3_82e0, 1184 | 0x15ce_2ead_2fcd_051e, 1185 | 0x4f74_d74b_ac2e_e457, 1186 | 0x337f_5447_07c4_30f0, 1187 | ]), 1188 | ], 1189 | [ 1190 | vesta::Base::from_raw([ 1191 | 0x26de_98a8_736d_1d11, 1192 | 0x7d8e_471a_9fb9_5fef, 1193 | 0xac9d_91b0_930d_ac75, 1194 | 0x3499_7991_9015_394f, 1195 | ]), 1196 | vesta::Base::from_raw([ 1197 | 0xccfc_b618_31d5_c775, 1198 | 0x3bf9_3da6_fff3_1d95, 1199 | 0x2305_cd7a_921e_c5f1, 1200 | 0x027c_c4ef_e3fb_35dd, 1201 | ]), 1202 | vesta::Base::from_raw([ 1203 | 0xc3fa_2629_635d_27de, 1204 | 0x67f1_c6b7_3147_64af, 1205 | 0x61b7_1a36_9868_2ad2, 1206 | 0x037f_9f23_6595_4c5b, 1207 | ]), 1208 | ], 1209 | [ 1210 | vesta::Base::from_raw([ 1211 | 0x77c5_b024_8483_71ae, 1212 | 0x6041_4abe_362d_01c9, 1213 | 0x10f1_cc6d_f8b4_bcd7, 1214 | 0x1f69_7cac_4d07_feb7, 1215 | ]), 1216 | vesta::Base::from_raw([ 1217 | 0x786a_dd24_4aa0_ef29, 1218 | 0x3145_c478_0631_09d6, 1219 | 0x26e6_c851_fbd5_72a6, 1220 | 0x267a_750f_e5d7_cfbc, 1221 | ]), 1222 | vesta::Base::from_raw([ 1223 | 0x180e_2b4d_3e75_6f65, 1224 | 0xaf28_5fa8_2ce4_fae5, 1225 | 0x678c_9996_d9a4_72c8, 1226 | 0x0c91_feab_4a43_193a, 1227 | ]), 1228 | ], 1229 | [ 1230 | vesta::Base::from_raw([ 1231 | 0x79c4_7c57_3ac4_10f7, 1232 | 0x7e3b_83af_4a4b_a3ba, 1233 | 0x2186_c303_8ea0_5e69, 1234 | 0x1745_569a_0a3e_3014, 1235 | ]), 1236 | vesta::Base::from_raw([ 1237 | 0x1e03_8852_2696_191f, 1238 | 0xfdff_66c6_f3b5_ffe1, 1239 | 0xeca5_1207_78a5_6711, 1240 | 0x2986_3d54_6e7e_7c0d, 1241 | ]), 1242 | vesta::Base::from_raw([ 1243 | 0x2f22_5e63_66bf_e390, 1244 | 0xa79a_03df_8339_94c6, 1245 | 0xbf06_bae4_9ef8_53f6, 1246 | 0x1148_d6ab_2bd0_0192, 1247 | ]), 1248 | ], 1249 | [ 1250 | vesta::Base::from_raw([ 1251 | 0xf4f6_331a_8b26_5d15, 1252 | 0xf745_f45d_350d_41d4, 1253 | 0xe18b_1499_060d_a366, 1254 | 0x02e0_e121_b0f3_dfef, 1255 | ]), 1256 | vesta::Base::from_raw([ 1257 | 0x078a_e6aa_1510_54b7, 1258 | 0x6904_0173_6d44_a653, 1259 | 0xb89e_f73a_40a2_b274, 1260 | 0x0d0a_a46e_76a6_a278, 1261 | ]), 1262 | vesta::Base::from_raw([ 1263 | 0x9a4d_532c_7b6e_0958, 1264 | 0x392d_de71_0f1f_06db, 1265 | 0xeee5_45f3_fa6d_3d08, 1266 | 0x1394_3675_b04a_a986, 1267 | ]), 1268 | ], 1269 | [ 1270 | vesta::Base::from_raw([ 1271 | 0x961f_c818_dcbb_66b5, 1272 | 0xc9f2_b325_7530_dafe, 1273 | 0xd97a_11d6_3088_f5d9, 1274 | 0x2901_ec61_942d_34aa, 1275 | ]), 1276 | vesta::Base::from_raw([ 1277 | 0xfdf5_44b9_63d1_fdc7, 1278 | 0x22ff_a2a2_af9f_a3e3, 1279 | 0xf431_d544_34a3_e0cf, 1280 | 0x2020_4a21_05d2_2e7e, 1281 | ]), 1282 | vesta::Base::from_raw([ 1283 | 0x1211_b9e2_190d_6852, 1284 | 0xa004_abe8_e015_28c4, 1285 | 0x5c1e_3e9e_27a5_71c3, 1286 | 0x3a8a_6282_9512_1d5c, 1287 | ]), 1288 | ], 1289 | ]; 1290 | 1291 | // n: 255 1292 | // t: 3 1293 | // N: 765 1294 | // Result Algorithm 1: 1295 | // [True, 0] 1296 | // Result Algorithm 2: 1297 | // [True, None] 1298 | // Result Algorithm 3: 1299 | // [True, None] 1300 | // Prime number: 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 1301 | // MDS matrix: 1302 | pub(crate) const MDS: [[vesta::Base; 3]; 3] = [ 1303 | [ 1304 | vesta::Base::from_raw([ 1305 | 0xeb4f_1f74_2963_421f, 1306 | 0x5f71_0afc_43dd_c5f6, 1307 | 0x9191_3f56_cf21_af2b, 1308 | 0x1853_b497_7c6f_a227, 1309 | ]), 1310 | vesta::Base::from_raw([ 1311 | 0x45e5_1db6_ac6f_e4a7, 1312 | 0x5a0f_a4df_a500_bcad, 1313 | 0x63f4_84c1_0fcf_0586, 1314 | 0x3d83_1189_cfbb_c452, 1315 | ]), 1316 | vesta::Base::from_raw([ 1317 | 0xd188_37f9_8347_f137, 1318 | 0x3f89_65c7_8083_8a94, 1319 | 0x4ba8_8b9e_4017_19c0, 1320 | 0x3a0e_3f84_d3c1_77d8, 1321 | ]), 1322 | ], 1323 | [ 1324 | vesta::Base::from_raw([ 1325 | 0x84fd_7923_337c_f77e, 1326 | 0x2896_f8d0_fd5c_9a75, 1327 | 0x8e9d_c529_f471_8f83, 1328 | 0x35e2_6e39_8450_6279, 1329 | ]), 1330 | vesta::Base::from_raw([ 1331 | 0x3eb9_24f5_6fff_7908, 1332 | 0x3641_cecf_3a2a_5a8a, 1333 | 0x00cd_7dbe_a799_70ab, 1334 | 0x10a8_1663_02cb_753c, 1335 | ]), 1336 | vesta::Base::from_raw([ 1337 | 0xb672_27c1_a141_ae94, 1338 | 0x198e_1aee_777e_2521, 1339 | 0xf434_92ce_5121_4b00, 1340 | 0x314f_762a_506d_321b, 1341 | ]), 1342 | ], 1343 | [ 1344 | vesta::Base::from_raw([ 1345 | 0xabcb_d614_eaf5_eba1, 1346 | 0xa90f_28b0_cb31_76fb, 1347 | 0xcb2e_ab86_ef31_d915, 1348 | 0x07b8_5627_c832_782a, 1349 | ]), 1350 | vesta::Base::from_raw([ 1351 | 0xc255_efd0_06b5_db1c, 1352 | 0xb5d9_85dc_1630_a4b2, 1353 | 0x9756_4e1b_5d1a_c72f, 1354 | 0x2a2d_e13e_70f2_7e16, 1355 | ]), 1356 | vesta::Base::from_raw([ 1357 | 0xcffd_f529_3334_29fc, 1358 | 0x21e3_af7e_f123_32cd, 1359 | 0xfff5_40a8_7327_c7ce, 1360 | 0x2c60_94d1_c6e1_caba, 1361 | ]), 1362 | ], 1363 | ]; 1364 | 1365 | pub(crate) const MDS_INV: [[vesta::Base; 3]; 3] = [ 1366 | [ 1367 | vesta::Base::from_raw([ 1368 | 0xb204_ddc6_5e58_2044, 1369 | 0x47a6_0484_b0a9_9c91, 1370 | 0xcaf5_4d78_24c1_200e, 1371 | 0x36df_4950_21cf_7828, 1372 | ]), 1373 | vesta::Base::from_raw([ 1374 | 0x6a6b_94ad_aa0d_9c9e, 1375 | 0xe2cd_38b9_59d4_61ff, 1376 | 0xe43e_c4bf_3e0d_f00c, 1377 | 0x034f_beae_4650_c2c7, 1378 | ]), 1379 | vesta::Base::from_raw([ 1380 | 0xa862_7a02_8c1a_f7d6, 1381 | 0x841b_ebf1_a15b_746e, 1382 | 0x1fd5_6832_d0ab_5570, 1383 | 0x20a8_64d6_790f_7c1c, 1384 | ]), 1385 | ], 1386 | [ 1387 | vesta::Base::from_raw([ 1388 | 0x3470_d5c5_53bc_9d20, 1389 | 0x1f95_660f_eb5d_b121, 1390 | 0xdd31_97ac_c894_9076, 1391 | 0x2d08_703d_48ec_d7dc, 1392 | ]), 1393 | vesta::Base::from_raw([ 1394 | 0x6b5b_42b0_67d8_30f3, 1395 | 0x6169_b6fa_721a_470e, 1396 | 0xeff3_18a2_8983_158a, 1397 | 0x2db1_0ecd_507a_2f27, 1398 | ]), 1399 | vesta::Base::from_raw([ 1400 | 0xfbae_b537_d278_4760, 1401 | 0x0068_e709_07e7_089d, 1402 | 0x926a_5fc0_cc1e_f726, 1403 | 0x0c8a_58c0_6473_cdfa, 1404 | ]), 1405 | ], 1406 | [ 1407 | vesta::Base::from_raw([ 1408 | 0x3a5a_ca10_7129_6e61, 1409 | 0x4ad4_442e_96c9_d5e8, 1410 | 0x5432_f0c0_b908_a411, 1411 | 0x2a64_2dca_695d_744d, 1412 | ]), 1413 | vesta::Base::from_raw([ 1414 | 0x1bd9_bfcb_be02_5ff1, 1415 | 0x24f6_ad43_b703_ad90, 1416 | 0xebb7_238d_f00d_17e7, 1417 | 0x114e_c796_fb40_3f5f, 1418 | ]), 1419 | vesta::Base::from_raw([ 1420 | 0x67f0_642e_14a9_c3bf, 1421 | 0xf6a6_9176_7069_7a97, 1422 | 0x0408_110d_c66e_b147, 1423 | 0x2825_e067_5968_dbeb, 1424 | ]), 1425 | ], 1426 | ]; 1427 | -------------------------------------------------------------------------------- /src/primitives/poseidon/grain.rs: -------------------------------------------------------------------------------- 1 | //! The Grain LFSR in self-shrinking mode, as used by Poseidon. 2 | 3 | use std::marker::PhantomData; 4 | 5 | use crate::halo2::arithmetic::FieldExt; 6 | use bitvec::prelude::*; 7 | 8 | const STATE: usize = 80; 9 | 10 | #[derive(Debug, Clone, Copy)] 11 | pub(super) enum FieldType { 12 | /// GF(2^n) 13 | #[allow(dead_code)] 14 | Binary, 15 | /// GF(p) 16 | PrimeOrder, 17 | } 18 | 19 | impl FieldType { 20 | fn tag(&self) -> u8 { 21 | match self { 22 | FieldType::Binary => 0, 23 | FieldType::PrimeOrder => 1, 24 | } 25 | } 26 | } 27 | 28 | #[derive(Debug, Clone, Copy)] 29 | pub(super) enum SboxType { 30 | /// x^alpha 31 | Pow, 32 | /// x^(-1) 33 | #[allow(dead_code)] 34 | Inv, 35 | } 36 | 37 | impl SboxType { 38 | fn tag(&self) -> u8 { 39 | match self { 40 | SboxType::Pow => 0, 41 | SboxType::Inv => 1, 42 | } 43 | } 44 | } 45 | 46 | pub(super) struct Grain { 47 | state: BitArr!(for 80, in Msb0, u8), 48 | next_bit: usize, 49 | _field: PhantomData, 50 | } 51 | 52 | impl Grain { 53 | pub(super) fn new(sbox: SboxType, t: u16, r_f: u16, r_p: u16) -> Self { 54 | // Initialize the LFSR state. 55 | let mut state = bitarr![Msb0, u8; 1; STATE]; 56 | let mut set_bits = |offset: usize, len, value| { 57 | // Poseidon reference impl sets initial state bits in MSB order. 58 | for i in 0..len { 59 | *state.get_mut(offset + len - 1 - i).unwrap() = (value >> i) & 1 != 0; 60 | } 61 | }; 62 | set_bits(0, 2, FieldType::PrimeOrder.tag() as u16); 63 | set_bits(2, 4, sbox.tag() as u16); 64 | set_bits(6, 12, F::NUM_BITS as u16); 65 | set_bits(18, 12, t); 66 | set_bits(30, 10, r_f); 67 | set_bits(40, 10, r_p); 68 | 69 | let mut grain = Grain { 70 | state, 71 | next_bit: STATE, 72 | _field: PhantomData::default(), 73 | }; 74 | 75 | // Discard the first 160 bits. 76 | for _ in 0..20 { 77 | grain.load_next_8_bits(); 78 | grain.next_bit = STATE; 79 | } 80 | 81 | grain 82 | } 83 | 84 | fn load_next_8_bits(&mut self) { 85 | let mut new_bits = 0u8; 86 | for i in 0..8 { 87 | new_bits |= ((self.state[i + 62] 88 | ^ self.state[i + 51] 89 | ^ self.state[i + 38] 90 | ^ self.state[i + 23] 91 | ^ self.state[i + 13] 92 | ^ self.state[i]) as u8) 93 | << i; 94 | } 95 | self.state.rotate_left(8); 96 | self.next_bit -= 8; 97 | for i in 0..8 { 98 | *self.state.get_mut(self.next_bit + i).unwrap() = (new_bits >> i) & 1 != 0; 99 | } 100 | } 101 | 102 | fn get_next_bit(&mut self) -> bool { 103 | if self.next_bit == STATE { 104 | self.load_next_8_bits(); 105 | } 106 | let ret = self.state[self.next_bit]; 107 | self.next_bit += 1; 108 | ret 109 | } 110 | 111 | /// Returns the next field element from this Grain instantiation. 112 | pub(super) fn next_field_element(&mut self) -> F { 113 | // Loop until we get an element in the field. 114 | loop { 115 | let mut bytes = F::Repr::default(); 116 | 117 | // Poseidon reference impl interprets the bits as a repr in MSB order, because 118 | // it's easy to do that in Python. Meanwhile, our field elements all use LSB 119 | // order. There's little motivation to diverge from the reference impl; these 120 | // are all constants, so we aren't introducing big-endianness into the rest of 121 | // the circuit (assuming unkeyed Poseidon, but we probably wouldn't want to 122 | // implement Grain inside a circuit, so we'd use a different round constant 123 | // derivation function there). 124 | let view = bytes.as_mut(); 125 | for (i, bit) in self.take(F::NUM_BITS as usize).enumerate() { 126 | // If we diverged from the reference impl and interpreted the bits in LSB 127 | // order, we would remove this line. 128 | let i = F::NUM_BITS as usize - 1 - i; 129 | 130 | view[i / 8] |= if bit { 1 << (i % 8) } else { 0 }; 131 | } 132 | 133 | if let Some(f) = F::from_repr_vartime(bytes) { 134 | break f; 135 | } 136 | } 137 | } 138 | 139 | /// Returns the next field element from this Grain instantiation, without using 140 | /// rejection sampling. 141 | pub(super) fn next_field_element_without_rejection(&mut self) -> F { 142 | let mut bytes = [0u8; 64]; 143 | 144 | // Poseidon reference impl interprets the bits as a repr in MSB order, because 145 | // it's easy to do that in Python. Additionally, it does not use rejection 146 | // sampling in cases where the constants don't specifically need to be uniformly 147 | // random for security. We do not provide APIs that take a field-element-sized 148 | // array and reduce it modulo the field order, because those are unsafe APIs to 149 | // offer generally (accidentally using them can lead to divergence in consensus 150 | // systems due to not rejecting canonical forms). 151 | // 152 | // Given that we don't want to diverge from the reference implementation, we hack 153 | // around this restriction by serializing the bits into a 64-byte array and then 154 | // calling F::from_bytes_wide. PLEASE DO NOT COPY THIS INTO YOUR OWN CODE! 155 | let view = bytes.as_mut(); 156 | for (i, bit) in self.take(F::NUM_BITS as usize).enumerate() { 157 | // If we diverged from the reference impl and interpreted the bits in LSB 158 | // order, we would remove this line. 159 | let i = F::NUM_BITS as usize - 1 - i; 160 | 161 | view[i / 8] |= if bit { 1 << (i % 8) } else { 0 }; 162 | } 163 | 164 | F::from_bytes_wide(&bytes) 165 | } 166 | } 167 | 168 | impl Iterator for Grain { 169 | type Item = bool; 170 | 171 | fn next(&mut self) -> Option { 172 | // Evaluate bits in pairs: 173 | // - If the first bit is a 1, output the second bit. 174 | // - If the first bit is a 0, discard the second bit. 175 | while !self.get_next_bit() { 176 | self.get_next_bit(); 177 | } 178 | Some(self.get_next_bit()) 179 | } 180 | } 181 | 182 | #[cfg(test)] 183 | mod tests { 184 | use pasta_curves::Fp; 185 | 186 | use super::{Grain, SboxType}; 187 | 188 | #[test] 189 | fn grain() { 190 | let mut grain = Grain::::new(SboxType::Pow, 3, 8, 56); 191 | let _f = grain.next_field_element(); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/primitives/poseidon/mds.rs: -------------------------------------------------------------------------------- 1 | use crate::halo2::arithmetic::FieldExt; 2 | 3 | use super::{grain::Grain, Mds}; 4 | 5 | pub(super) fn generate_mds( 6 | grain: &mut Grain, 7 | mut select: usize, 8 | ) -> (Mds, Mds) { 9 | let (xs, ys, mds) = loop { 10 | // Generate two [F; T] arrays of unique field elements. 11 | let (xs, ys) = loop { 12 | let mut vals: Vec<_> = (0..2 * T) 13 | .map(|_| grain.next_field_element_without_rejection()) 14 | .collect(); 15 | 16 | // Check that we have unique field elements. 17 | let mut unique = vals.clone(); 18 | unique.sort_unstable(); 19 | unique.dedup(); 20 | if vals.len() == unique.len() { 21 | let rhs = vals.split_off(T); 22 | break (vals, rhs); 23 | } 24 | }; 25 | 26 | // We need to ensure that the MDS is secure. Instead of checking the MDS against 27 | // the relevant algorithms directly, we witness a fixed number of MDS matrices 28 | // that we need to sample from the given Grain state before obtaining a secure 29 | // matrix. This can be determined out-of-band via the reference implementation in 30 | // Sage. 31 | if select != 0 { 32 | select -= 1; 33 | continue; 34 | } 35 | 36 | // Generate a Cauchy matrix, with elements a_ij in the form: 37 | // a_ij = 1/(x_i + y_j); x_i + y_j != 0 38 | // 39 | // It would be much easier to use the alternate definition: 40 | // a_ij = 1/(x_i - y_j); x_i - y_j != 0 41 | // 42 | // These are clearly equivalent on `y <- -y`, but it is easier to work with the 43 | // negative formulation, because ensuring that xs ∪ ys is unique implies that 44 | // x_i - y_j != 0 by construction (whereas the positive case does not hold). It 45 | // also makes computation of the matrix inverse simpler below (the theorem used 46 | // was formulated for the negative definition). 47 | // 48 | // However, the Poseidon paper and reference impl use the positive formulation, 49 | // and we want to rely on the reference impl for MDS security, so we use the same 50 | // formulation. 51 | let mut mds = [[F::zero(); T]; T]; 52 | #[allow(clippy::needless_range_loop)] 53 | for i in 0..T { 54 | for j in 0..T { 55 | let sum = xs[i] + ys[j]; 56 | // We leverage the secure MDS selection counter to also check this. 57 | assert!(!sum.is_zero_vartime()); 58 | mds[i][j] = sum.invert().unwrap(); 59 | } 60 | } 61 | 62 | break (xs, ys, mds); 63 | }; 64 | 65 | // Compute the inverse. All square Cauchy matrices have a non-zero determinant and 66 | // thus are invertible. The inverse for a Cauchy matrix of the form: 67 | // 68 | // a_ij = 1/(x_i - y_j); x_i - y_j != 0 69 | // 70 | // has elements b_ij given by: 71 | // 72 | // b_ij = (x_j - y_i) A_j(y_i) B_i(x_j) (Schechter 1959, Theorem 1) 73 | // 74 | // where A_i(x) and B_i(x) are the Lagrange polynomials for xs and ys respectively. 75 | // 76 | // We adapt this to the positive Cauchy formulation by negating ys. 77 | let mut mds_inv = [[F::zero(); T]; T]; 78 | let l = |xs: &[F], j, x: F| { 79 | let x_j = xs[j]; 80 | xs.iter().enumerate().fold(F::one(), |acc, (m, x_m)| { 81 | if m == j { 82 | acc 83 | } else { 84 | // We can invert freely; by construction, the elements of xs are distinct. 85 | acc * (x - x_m) * (x_j - x_m).invert().unwrap() 86 | } 87 | }) 88 | }; 89 | let neg_ys: Vec<_> = ys.iter().map(|y| -*y).collect(); 90 | for i in 0..T { 91 | for j in 0..T { 92 | mds_inv[i][j] = (xs[j] - neg_ys[i]) * l(&xs, j, neg_ys[i]) * l(&neg_ys, i, xs[j]); 93 | } 94 | } 95 | 96 | (mds, mds_inv) 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use pasta_curves::Fp; 102 | 103 | use super::{generate_mds, Grain}; 104 | 105 | #[test] 106 | fn poseidon_mds() { 107 | const T: usize = 3; 108 | let mut grain = Grain::new(super::super::grain::SboxType::Pow, T as u16, 8, 56); 109 | let (mds, mds_inv) = generate_mds::(&mut grain, 0); 110 | 111 | // Verify that MDS * MDS^-1 = I. 112 | #[allow(clippy::needless_range_loop)] 113 | for i in 0..T { 114 | for j in 0..T { 115 | let expected = if i == j { Fp::one() } else { Fp::zero() }; 116 | assert_eq!( 117 | (0..T).fold(Fp::zero(), |acc, k| acc + (mds[i][k] * mds_inv[k][j])), 118 | expected 119 | ); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/primitives/poseidon/p128pow5t3.rs: -------------------------------------------------------------------------------- 1 | use crate::halo2::arithmetic::Field; 2 | use pasta_curves::{pallas::Base as Fp, vesta::Base as Fq}; 3 | 4 | use super::{Mds, Spec}; 5 | 6 | /// Poseidon-128 using the $x^5$ S-box, with a width of 3 field elements, and the 7 | /// standard number of rounds for 128-bit security "with margin". 8 | /// 9 | /// The standard specification for this set of parameters (on either of the Pasta 10 | /// fields) uses $R_F = 8, R_P = 56$. This is conveniently an even number of 11 | /// partial rounds, making it easier to construct a Halo 2 circuit. 12 | #[derive(Debug)] 13 | pub struct P128Pow5T3; 14 | 15 | impl Spec for P128Pow5T3 { 16 | fn full_rounds() -> usize { 17 | 8 18 | } 19 | 20 | fn partial_rounds() -> usize { 21 | 56 22 | } 23 | 24 | fn sbox(val: Fp) -> Fp { 25 | val.pow_vartime(&[5]) 26 | } 27 | 28 | fn secure_mds(&self) -> usize { 29 | unimplemented!() 30 | } 31 | 32 | fn constants(&self) -> (Vec<[Fp; 3]>, Mds, Mds) { 33 | ( 34 | super::fp::ROUND_CONSTANTS[..].to_vec(), 35 | super::fp::MDS, 36 | super::fp::MDS_INV, 37 | ) 38 | } 39 | } 40 | 41 | impl Spec for P128Pow5T3 { 42 | fn full_rounds() -> usize { 43 | 8 44 | } 45 | 46 | fn partial_rounds() -> usize { 47 | 56 48 | } 49 | 50 | fn sbox(val: Fq) -> Fq { 51 | val.pow_vartime(&[5]) 52 | } 53 | 54 | fn secure_mds(&self) -> usize { 55 | unimplemented!() 56 | } 57 | 58 | fn constants(&self) -> (Vec<[Fq; 3]>, Mds, Mds) { 59 | ( 60 | super::fq::ROUND_CONSTANTS[..].to_vec(), 61 | super::fq::MDS, 62 | super::fq::MDS_INV, 63 | ) 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use std::marker::PhantomData; 70 | 71 | use ff::PrimeField; 72 | use pasta_curves::arithmetic::FieldExt; 73 | 74 | use super::{ 75 | super::{fp, fq}, 76 | Fp, Fq, 77 | }; 78 | use crate::primitives::poseidon::{permute, ConstantLength, Hash, Spec}; 79 | 80 | /// The same Poseidon specification as poseidon::P128Pow5T3, but constructed 81 | /// such that its constants will be generated at runtime. 82 | #[derive(Debug)] 83 | pub struct P128Pow5T3Gen { 84 | secure_mds: usize, 85 | _field: PhantomData, 86 | } 87 | 88 | impl P128Pow5T3Gen { 89 | pub fn new(secure_mds: usize) -> Self { 90 | P128Pow5T3Gen { 91 | secure_mds, 92 | _field: PhantomData::default(), 93 | } 94 | } 95 | } 96 | 97 | impl Spec for P128Pow5T3Gen { 98 | fn full_rounds() -> usize { 99 | 8 100 | } 101 | 102 | fn partial_rounds() -> usize { 103 | 56 104 | } 105 | 106 | fn sbox(val: F) -> F { 107 | val.pow_vartime(&[5]) 108 | } 109 | 110 | fn secure_mds(&self) -> usize { 111 | self.secure_mds 112 | } 113 | } 114 | 115 | #[test] 116 | fn verify_constants() { 117 | fn verify_constants_helper( 118 | expected_round_constants: [[F; 3]; 64], 119 | expected_mds: [[F; 3]; 3], 120 | expected_mds_inv: [[F; 3]; 3], 121 | ) { 122 | let poseidon = P128Pow5T3Gen::::new(0); 123 | let (round_constants, mds, mds_inv) = poseidon.constants(); 124 | 125 | for (actual, expected) in round_constants 126 | .iter() 127 | .flatten() 128 | .zip(expected_round_constants.iter().flatten()) 129 | { 130 | assert_eq!(actual, expected); 131 | } 132 | 133 | for (actual, expected) in mds.iter().flatten().zip(expected_mds.iter().flatten()) { 134 | assert_eq!(actual, expected); 135 | } 136 | 137 | for (actual, expected) in mds_inv 138 | .iter() 139 | .flatten() 140 | .zip(expected_mds_inv.iter().flatten()) 141 | { 142 | assert_eq!(actual, expected); 143 | } 144 | } 145 | 146 | verify_constants_helper(fp::ROUND_CONSTANTS, fp::MDS, fp::MDS_INV); 147 | verify_constants_helper(fq::ROUND_CONSTANTS, fq::MDS, fq::MDS_INV); 148 | } 149 | 150 | #[test] 151 | fn test_against_reference() { 152 | { 153 | // , using parameters from 154 | // `generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001`. 155 | // The test vector is generated by `sage poseidonperm_x5_pallas_3.sage --rust` 156 | 157 | let mut input = [ 158 | Fp::from_raw([ 159 | 0x0000_0000_0000_0000, 160 | 0x0000_0000_0000_0000, 161 | 0x0000_0000_0000_0000, 162 | 0x0000_0000_0000_0000, 163 | ]), 164 | Fp::from_raw([ 165 | 0x0000_0000_0000_0001, 166 | 0x0000_0000_0000_0000, 167 | 0x0000_0000_0000_0000, 168 | 0x0000_0000_0000_0000, 169 | ]), 170 | Fp::from_raw([ 171 | 0x0000_0000_0000_0002, 172 | 0x0000_0000_0000_0000, 173 | 0x0000_0000_0000_0000, 174 | 0x0000_0000_0000_0000, 175 | ]), 176 | ]; 177 | 178 | let expected_output = [ 179 | Fp::from_raw([ 180 | 0xaeb1_bc02_4aec_a456, 181 | 0xf7e6_9a71_d0b6_42a0, 182 | 0x94ef_b364_f966_240f, 183 | 0x2a52_6acd_0b64_b453, 184 | ]), 185 | Fp::from_raw([ 186 | 0x012a_3e96_28e5_b82a, 187 | 0xdcd4_2e7f_bed9_dafe, 188 | 0x76ff_7dae_343d_5512, 189 | 0x13c5_d156_8b4a_a430, 190 | ]), 191 | Fp::from_raw([ 192 | 0x3590_29a1_d34e_9ddd, 193 | 0xf7cf_dfe1_bda4_2c7b, 194 | 0x256f_cd59_7984_561a, 195 | 0x0a49_c868_c697_6544, 196 | ]), 197 | ]; 198 | 199 | permute::, 3, 2>(&mut input, &fp::MDS, &fp::ROUND_CONSTANTS); 200 | assert_eq!(input, expected_output); 201 | } 202 | 203 | { 204 | // , using parameters from 205 | // `generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001`. 206 | // The test vector is generated by `sage poseidonperm_x5_vesta_3.sage --rust` 207 | 208 | let mut input = [ 209 | Fq::from_raw([ 210 | 0x0000_0000_0000_0000, 211 | 0x0000_0000_0000_0000, 212 | 0x0000_0000_0000_0000, 213 | 0x0000_0000_0000_0000, 214 | ]), 215 | Fq::from_raw([ 216 | 0x0000_0000_0000_0001, 217 | 0x0000_0000_0000_0000, 218 | 0x0000_0000_0000_0000, 219 | 0x0000_0000_0000_0000, 220 | ]), 221 | Fq::from_raw([ 222 | 0x0000_0000_0000_0002, 223 | 0x0000_0000_0000_0000, 224 | 0x0000_0000_0000_0000, 225 | 0x0000_0000_0000_0000, 226 | ]), 227 | ]; 228 | 229 | let expected_output = [ 230 | Fq::from_raw([ 231 | 0x0eb0_8ea8_13be_be59, 232 | 0x4d43_d197_3dd3_36c6, 233 | 0xeddd_74f2_2f8f_2ff7, 234 | 0x315a_1f4c_db94_2f7c, 235 | ]), 236 | Fq::from_raw([ 237 | 0xf9f1_26e6_1ea1_65f1, 238 | 0x413e_e0eb_7bbd_2198, 239 | 0x642a_dee0_dd13_aa48, 240 | 0x3be4_75f2_d764_2bde, 241 | ]), 242 | Fq::from_raw([ 243 | 0x14d5_4237_2a7b_a0d9, 244 | 0x5019_bfd4_e042_3fa0, 245 | 0x117f_db24_20d8_ea60, 246 | 0x25ab_8aec_e953_7168, 247 | ]), 248 | ]; 249 | 250 | permute::, 3, 2>(&mut input, &fq::MDS, &fq::ROUND_CONSTANTS); 251 | assert_eq!(input, expected_output); 252 | } 253 | } 254 | 255 | #[test] 256 | fn permute_test_vectors() { 257 | { 258 | let (round_constants, mds, _) = super::P128Pow5T3.constants(); 259 | 260 | for tv in crate::primitives::poseidon::test_vectors::fp::permute() { 261 | let mut state = [ 262 | Fp::from_repr(tv.initial_state[0]).unwrap(), 263 | Fp::from_repr(tv.initial_state[1]).unwrap(), 264 | Fp::from_repr(tv.initial_state[2]).unwrap(), 265 | ]; 266 | 267 | permute::(&mut state, &mds, &round_constants); 268 | 269 | for (expected, actual) in tv.final_state.iter().zip(state.iter()) { 270 | assert_eq!(&actual.to_repr(), expected); 271 | } 272 | } 273 | } 274 | 275 | { 276 | let (round_constants, mds, _) = super::P128Pow5T3.constants(); 277 | 278 | for tv in crate::primitives::poseidon::test_vectors::fq::permute() { 279 | let mut state = [ 280 | Fq::from_repr(tv.initial_state[0]).unwrap(), 281 | Fq::from_repr(tv.initial_state[1]).unwrap(), 282 | Fq::from_repr(tv.initial_state[2]).unwrap(), 283 | ]; 284 | 285 | permute::(&mut state, &mds, &round_constants); 286 | 287 | for (expected, actual) in tv.final_state.iter().zip(state.iter()) { 288 | assert_eq!(&actual.to_repr(), expected); 289 | } 290 | } 291 | } 292 | } 293 | 294 | #[test] 295 | fn hash_test_vectors() { 296 | for tv in crate::primitives::poseidon::test_vectors::fp::hash() { 297 | let message = [ 298 | Fp::from_repr(tv.input[0]).unwrap(), 299 | Fp::from_repr(tv.input[1]).unwrap(), 300 | ]; 301 | 302 | let result = Hash::init(super::P128Pow5T3, ConstantLength).hash(message); 303 | 304 | assert_eq!(result.to_repr(), tv.output); 305 | } 306 | 307 | for tv in crate::primitives::poseidon::test_vectors::fq::hash() { 308 | let message = [ 309 | Fq::from_repr(tv.input[0]).unwrap(), 310 | Fq::from_repr(tv.input[1]).unwrap(), 311 | ]; 312 | 313 | let result = Hash::init(super::P128Pow5T3, ConstantLength).hash(message); 314 | 315 | assert_eq!(result.to_repr(), tv.output); 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | reorder_modules = true 3 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::halo2::pasta::Fp; 4 | 5 | use crate::{ 6 | franchise::FranchiseCircuit, 7 | primitives::poseidon::{self, ConstantLength, P128Pow5T3}, 8 | }; 9 | 10 | pub struct MerkleTreeBuilder { 11 | depth: u32, 12 | nodes: Vec, 13 | } 14 | 15 | pub struct MerkleTree { 16 | depth: u32, 17 | nodes: Vec, 18 | } 19 | 20 | impl MerkleTreeBuilder { 21 | pub fn new(depth: u32) -> Self { 22 | let size = 2usize.pow(depth - 1); 23 | Self { 24 | depth, 25 | nodes: Vec::with_capacity(2 * size - 1), 26 | } 27 | } 28 | pub fn insert(&mut self, value: Fp) -> usize { 29 | assert!(self.nodes.len() < 2usize.pow(self.depth - 1)); 30 | self.nodes.push(value); 31 | self.nodes.len() - 1 32 | } 33 | 34 | fn hash(first: Fp, second: Fp) -> Fp { 35 | poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash([first, second]) 36 | } 37 | 38 | pub fn build(self) -> MerkleTree { 39 | let MerkleTreeBuilder { depth, mut nodes } = self; 40 | 41 | // fill with zeroes the unused leafs 42 | let size = 2usize.pow(self.depth - 1); 43 | if nodes.len() < size { 44 | nodes.resize(size, Fp::zero()); 45 | } 46 | 47 | // compute the merkle tree nodes 48 | let mut i = 0; 49 | while i < nodes.capacity() - 1 { 50 | nodes.push(Self::hash(nodes[i], nodes[i + 1])); 51 | i += 2; 52 | } 53 | 54 | MerkleTree { depth, nodes } 55 | } 56 | } 57 | 58 | impl MerkleTree { 59 | pub fn print_tree(&self) { 60 | let mut pos = (self.nodes.len() - 1) as isize; 61 | let mut lvl = 1; 62 | while pos >= 0 { 63 | for l in 0..lvl { 64 | let s = format!("{:?}", self.nodes[(pos + l) as usize]); 65 | print!("{} ", &s[60..66]); 66 | } 67 | println!(); 68 | pos -= lvl * 2; 69 | lvl *= 2; 70 | } 71 | } 72 | 73 | pub fn root(&self) -> Fp { 74 | self.nodes[self.nodes.len() - 1] 75 | } 76 | pub fn get(&self, index: usize) -> Fp { 77 | self.nodes[index] 78 | } 79 | 80 | pub fn witness(&self, mut index: usize) -> Vec<(Fp, bool)> { 81 | let mut base = 0; 82 | let mut siblings = Vec::new(); 83 | for n in 0..self.depth - 1 { 84 | let left_right = 1 - (index & 1); 85 | siblings.push(( 86 | self.nodes[base + (index & 0xfffe) + left_right], 87 | left_right == 1, 88 | )); 89 | base += 2usize.pow(self.depth - n - 1); 90 | index >>= 1; 91 | } 92 | siblings 93 | } 94 | 95 | pub fn check_witness(value: Fp, siblings: Vec<(Fp, bool)>, root: Fp) -> bool { 96 | let mut hash = value; 97 | for (value, order) in siblings { 98 | hash = if order { 99 | MerkleTreeBuilder::hash(hash, value) 100 | } else { 101 | MerkleTreeBuilder::hash(value, hash) 102 | }; 103 | } 104 | hash == root 105 | } 106 | } 107 | 108 | pub fn generate_circuit_inputs( 109 | secret_key: Fp, 110 | process_id: [Fp; 2], 111 | vote_hash: Fp, 112 | witness: &[(Fp, bool)], 113 | ) -> (FranchiseCircuit, Fp) { 114 | let process_id_hash = 115 | poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash([process_id[0], process_id[1]]); 116 | 117 | let pub_nullifier = 118 | poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash([secret_key, process_id_hash]); 119 | 120 | let mut pri_siblings = [Fp::zero(); LVL]; 121 | let mut pri_index = [false; LVL]; 122 | for (n, (l, p)) in witness.iter().enumerate() { 123 | pri_siblings[n] = *l; 124 | pri_index[n] = !p; 125 | } 126 | 127 | let circuit = FranchiseCircuit { 128 | pri_index: Some(pri_index), 129 | pri_siblings: Some(pri_siblings), 130 | pri_secret_key: Some(secret_key), 131 | pub_processid: Some(process_id), 132 | pub_votehash: Some(vote_hash), 133 | }; 134 | 135 | (circuit, pub_nullifier) 136 | } 137 | 138 | pub fn generate_test_data() -> (FranchiseCircuit, Vec) { 139 | let secret_key = Fp::from(8); 140 | let process_id = [Fp::from(6), Fp::from(7)]; 141 | let vote_hash = Fp::from(1); 142 | let public_key = secret_to_public_key(secret_key); 143 | 144 | let mut root = public_key; 145 | let mut witness = Vec::new(); 146 | for n in 0..LVL as u64 { 147 | let direction = n % 2 == 0; 148 | let value = Fp::from(n); 149 | let (left, right) = if direction { 150 | (root, value) 151 | } else { 152 | (value, root) 153 | }; 154 | 155 | let digest = poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash([left, right]); 156 | witness.push((value, direction)); 157 | root = digest; 158 | } 159 | assert!(MerkleTree::check_witness(public_key, witness.clone(), root)); 160 | 161 | let (circuit, nullifier) = 162 | generate_circuit_inputs::(secret_key, process_id, vote_hash, &witness); 163 | 164 | (circuit, vec![root, nullifier, vote_hash]) 165 | } 166 | 167 | pub fn secret_to_public_key(secret_key: Fp) -> Fp { 168 | poseidon::Hash::init(P128Pow5T3, ConstantLength::<2>).hash([Fp::one(), secret_key]) 169 | } 170 | 171 | #[test] 172 | fn simple_mt_test() { 173 | let mut tree = MerkleTreeBuilder::new(6); 174 | for n in 0..2u64.pow(tree.depth - 1) { 175 | tree.insert(Fp::from(n)); 176 | } 177 | let tree = tree.build(); 178 | tree.print_tree(); 179 | for n in 0..2usize.pow(tree.depth - 1) { 180 | let witness = tree.witness(n); 181 | assert!(MerkleTree::check_witness(tree.get(n), witness, tree.root())); 182 | } 183 | } 184 | --------------------------------------------------------------------------------