├── .babelrc ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── README.md ├── cli └── cli.py ├── docker-compose.yml ├── package.json ├── programs ├── .gitignore ├── compile_and_run.sh ├── lib │ ├── bytes_utils.cairo │ ├── keccak.cairo │ ├── packed_keccak.cairo │ ├── secp │ │ ├── bigint.cairo │ │ ├── secp.cairo │ │ ├── secp_def.cairo │ │ ├── secp_ec.cairo │ │ └── secp_field.cairo │ └── storage_verification │ │ ├── address.cairo │ │ ├── bitset.cairo │ │ ├── bitshift.cairo │ │ ├── blockheader_rlp_extractor.cairo │ │ ├── comp_arr.cairo │ │ ├── concat_arr.cairo │ │ ├── extract_from_rlp.cairo │ │ ├── keccak.cairo │ │ ├── packed_keccak.cairo │ │ ├── pow.cairo │ │ ├── swap_endianness.cairo │ │ ├── trie_proofs.cairo │ │ ├── types.cairo │ │ ├── words64.cairo │ │ └── xor_state.cairo ├── storage_proof.cairo └── storage_proof.json ├── public ├── images │ ├── eth-diamond-black.png │ ├── eth-diamond-glyph.png │ ├── eth-diamond-purple.png │ ├── eth-diamond-rainbow.webp │ └── milad-fakurian-iFu2HILEng8-unsplash.jpg ├── index.html └── manifest.json ├── src ├── App.tsx ├── components │ ├── Badge │ │ └── Badge.tsx │ ├── Header │ │ └── Header.tsx │ ├── Table │ │ └── Table.tsx │ └── Wallet │ │ └── Ethereum.tsx ├── connectors │ └── metaMask.ts ├── index.tsx ├── lib.rs ├── pages │ ├── Badges │ │ ├── ViewAccountPage.tsx │ │ └── ViewBadgePage.tsx │ ├── Home │ │ └── HomePage.tsx │ └── Mint │ │ └── MintPage.tsx └── theme.tsx ├── tsconfig.json ├── webpack.config.ts └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react", 5 | "@babel/preset-typescript" 6 | ], 7 | "plugins": [ 8 | [ 9 | "@babel/plugin-transform-runtime", 10 | { 11 | "regenerator": true 12 | } 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | pkg/ 4 | build/* 5 | node_modules 6 | target 7 | -------------------------------------------------------------------------------- /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 = "addchain" 7 | version = "0.2.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "3b2e69442aa5628ea6951fa33e24efe8313f4321a91bd729fc2f75bdfc858570" 10 | dependencies = [ 11 | "num-bigint", 12 | "num-integer", 13 | "num-traits", 14 | ] 15 | 16 | [[package]] 17 | name = "arrayref" 18 | version = "0.3.6" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 21 | 22 | [[package]] 23 | name = "arrayvec" 24 | version = "0.7.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 27 | 28 | [[package]] 29 | name = "autocfg" 30 | version = "1.1.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 33 | 34 | [[package]] 35 | name = "bincode" 36 | version = "1.3.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 39 | dependencies = [ 40 | "serde", 41 | ] 42 | 43 | [[package]] 44 | name = "bitvec" 45 | version = "1.0.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b" 48 | dependencies = [ 49 | "funty", 50 | "radium", 51 | "tap", 52 | "wyz", 53 | ] 54 | 55 | [[package]] 56 | name = "blake3" 57 | version = "1.3.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" 60 | dependencies = [ 61 | "arrayref", 62 | "arrayvec", 63 | "cc", 64 | "cfg-if", 65 | "constant_time_eq", 66 | ] 67 | 68 | [[package]] 69 | name = "block-buffer" 70 | version = "0.10.2" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" 73 | dependencies = [ 74 | "generic-array", 75 | ] 76 | 77 | [[package]] 78 | name = "bumpalo" 79 | version = "3.10.0" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" 82 | 83 | [[package]] 84 | name = "byteorder" 85 | version = "1.4.3" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 88 | 89 | [[package]] 90 | name = "cc" 91 | version = "1.0.73" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 94 | 95 | [[package]] 96 | name = "cfg-if" 97 | version = "1.0.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 100 | 101 | [[package]] 102 | name = "constant_time_eq" 103 | version = "0.1.5" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 106 | 107 | [[package]] 108 | name = "crypto-common" 109 | version = "0.1.3" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" 112 | dependencies = [ 113 | "generic-array", 114 | "typenum", 115 | ] 116 | 117 | [[package]] 118 | name = "digest" 119 | version = "0.10.3" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" 122 | dependencies = [ 123 | "block-buffer", 124 | "crypto-common", 125 | ] 126 | 127 | [[package]] 128 | name = "ff" 129 | version = "0.12.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" 132 | dependencies = [ 133 | "bitvec", 134 | "byteorder", 135 | "ff_derive", 136 | "rand_core", 137 | "subtle", 138 | ] 139 | 140 | [[package]] 141 | name = "ff_derive" 142 | version = "0.12.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "95dddd96655a8ef19cc0dd2cb7063fb159f1583af0dbf3c04805ebc7dcc15849" 145 | dependencies = [ 146 | "addchain", 147 | "cfg-if", 148 | "num-bigint", 149 | "num-integer", 150 | "num-traits", 151 | "proc-macro2", 152 | "quote", 153 | "syn", 154 | ] 155 | 156 | [[package]] 157 | name = "funty" 158 | version = "2.0.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 161 | 162 | [[package]] 163 | name = "generic-array" 164 | version = "0.14.5" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" 167 | dependencies = [ 168 | "typenum", 169 | "version_check", 170 | ] 171 | 172 | [[package]] 173 | name = "giza-air" 174 | version = "0.1.0" 175 | source = "git+https://github.com/maxgillett/giza?rev=ce65e65#ce65e652d02fd7995cfca4bf76ca7b592bbc1813" 176 | dependencies = [ 177 | "giza-core", 178 | "serde", 179 | "winter-air", 180 | "winter-utils", 181 | ] 182 | 183 | [[package]] 184 | name = "giza-core" 185 | version = "0.1.0" 186 | source = "git+https://github.com/maxgillett/giza?rev=ce65e65#ce65e652d02fd7995cfca4bf76ca7b592bbc1813" 187 | dependencies = [ 188 | "ff", 189 | "hex", 190 | "winter-math", 191 | "winter-utils", 192 | ] 193 | 194 | [[package]] 195 | name = "hex" 196 | version = "0.4.3" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 199 | 200 | [[package]] 201 | name = "js-sys" 202 | version = "0.3.57" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" 205 | dependencies = [ 206 | "wasm-bindgen", 207 | ] 208 | 209 | [[package]] 210 | name = "keccak" 211 | version = "0.1.2" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" 214 | 215 | [[package]] 216 | name = "lazy_static" 217 | version = "1.4.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 220 | 221 | [[package]] 222 | name = "log" 223 | version = "0.4.17" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 226 | dependencies = [ 227 | "cfg-if", 228 | ] 229 | 230 | [[package]] 231 | name = "num-bigint" 232 | version = "0.3.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" 235 | dependencies = [ 236 | "autocfg", 237 | "num-integer", 238 | "num-traits", 239 | ] 240 | 241 | [[package]] 242 | name = "num-integer" 243 | version = "0.1.45" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 246 | dependencies = [ 247 | "autocfg", 248 | "num-traits", 249 | ] 250 | 251 | [[package]] 252 | name = "num-traits" 253 | version = "0.2.15" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 256 | dependencies = [ 257 | "autocfg", 258 | ] 259 | 260 | [[package]] 261 | name = "proc-macro2" 262 | version = "1.0.39" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" 265 | dependencies = [ 266 | "unicode-ident", 267 | ] 268 | 269 | [[package]] 270 | name = "quote" 271 | version = "1.0.18" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" 274 | dependencies = [ 275 | "proc-macro2", 276 | ] 277 | 278 | [[package]] 279 | name = "radium" 280 | version = "0.7.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 283 | 284 | [[package]] 285 | name = "rand_core" 286 | version = "0.6.3" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 289 | 290 | [[package]] 291 | name = "serde" 292 | version = "1.0.137" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" 295 | dependencies = [ 296 | "serde_derive", 297 | ] 298 | 299 | [[package]] 300 | name = "serde_derive" 301 | version = "1.0.137" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" 304 | dependencies = [ 305 | "proc-macro2", 306 | "quote", 307 | "syn", 308 | ] 309 | 310 | [[package]] 311 | name = "sha3" 312 | version = "0.10.1" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" 315 | dependencies = [ 316 | "digest", 317 | "keccak", 318 | ] 319 | 320 | [[package]] 321 | name = "stark-attestations-wasm" 322 | version = "0.1.0" 323 | dependencies = [ 324 | "bincode", 325 | "giza-air", 326 | "giza-core", 327 | "hex", 328 | "js-sys", 329 | "serde", 330 | "sha3", 331 | "wasm-bindgen", 332 | "winter-crypto", 333 | "winter-math", 334 | "winter-utils", 335 | "winter-verifier", 336 | ] 337 | 338 | [[package]] 339 | name = "subtle" 340 | version = "2.4.1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 343 | 344 | [[package]] 345 | name = "syn" 346 | version = "1.0.96" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" 349 | dependencies = [ 350 | "proc-macro2", 351 | "quote", 352 | "unicode-ident", 353 | ] 354 | 355 | [[package]] 356 | name = "tap" 357 | version = "1.0.1" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 360 | 361 | [[package]] 362 | name = "typenum" 363 | version = "1.15.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 366 | 367 | [[package]] 368 | name = "unicode-ident" 369 | version = "1.0.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" 372 | 373 | [[package]] 374 | name = "version_check" 375 | version = "0.9.4" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 378 | 379 | [[package]] 380 | name = "wasm-bindgen" 381 | version = "0.2.80" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" 384 | dependencies = [ 385 | "cfg-if", 386 | "wasm-bindgen-macro", 387 | ] 388 | 389 | [[package]] 390 | name = "wasm-bindgen-backend" 391 | version = "0.2.80" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" 394 | dependencies = [ 395 | "bumpalo", 396 | "lazy_static", 397 | "log", 398 | "proc-macro2", 399 | "quote", 400 | "syn", 401 | "wasm-bindgen-shared", 402 | ] 403 | 404 | [[package]] 405 | name = "wasm-bindgen-macro" 406 | version = "0.2.80" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" 409 | dependencies = [ 410 | "quote", 411 | "wasm-bindgen-macro-support", 412 | ] 413 | 414 | [[package]] 415 | name = "wasm-bindgen-macro-support" 416 | version = "0.2.80" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" 419 | dependencies = [ 420 | "proc-macro2", 421 | "quote", 422 | "syn", 423 | "wasm-bindgen-backend", 424 | "wasm-bindgen-shared", 425 | ] 426 | 427 | [[package]] 428 | name = "wasm-bindgen-shared" 429 | version = "0.2.80" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" 432 | 433 | [[package]] 434 | name = "winter-air" 435 | version = "0.4.0" 436 | source = "git+https://github.com/maxgillett/winterfell?rev=0aad6a5#0aad6a5b3c49a252e1c732177111bbabc99eb9bd" 437 | dependencies = [ 438 | "winter-crypto", 439 | "winter-fri", 440 | "winter-math", 441 | "winter-utils", 442 | ] 443 | 444 | [[package]] 445 | name = "winter-crypto" 446 | version = "0.4.0" 447 | source = "git+https://github.com/maxgillett/winterfell?rev=0aad6a5#0aad6a5b3c49a252e1c732177111bbabc99eb9bd" 448 | dependencies = [ 449 | "blake3", 450 | "sha3", 451 | "winter-math", 452 | "winter-utils", 453 | ] 454 | 455 | [[package]] 456 | name = "winter-fri" 457 | version = "0.4.0" 458 | source = "git+https://github.com/maxgillett/winterfell?rev=0aad6a5#0aad6a5b3c49a252e1c732177111bbabc99eb9bd" 459 | dependencies = [ 460 | "winter-crypto", 461 | "winter-math", 462 | "winter-utils", 463 | ] 464 | 465 | [[package]] 466 | name = "winter-math" 467 | version = "0.4.0" 468 | source = "git+https://github.com/maxgillett/winterfell?rev=0aad6a5#0aad6a5b3c49a252e1c732177111bbabc99eb9bd" 469 | dependencies = [ 470 | "winter-utils", 471 | ] 472 | 473 | [[package]] 474 | name = "winter-utils" 475 | version = "0.4.0" 476 | source = "git+https://github.com/maxgillett/winterfell?rev=0aad6a5#0aad6a5b3c49a252e1c732177111bbabc99eb9bd" 477 | 478 | [[package]] 479 | name = "winter-verifier" 480 | version = "0.4.0" 481 | source = "git+https://github.com/maxgillett/winterfell?rev=0aad6a5#0aad6a5b3c49a252e1c732177111bbabc99eb9bd" 482 | dependencies = [ 483 | "winter-air", 484 | "winter-crypto", 485 | "winter-fri", 486 | "winter-math", 487 | "winter-utils", 488 | ] 489 | 490 | [[package]] 491 | name = "wyz" 492 | version = "0.5.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" 495 | dependencies = [ 496 | "tap", 497 | ] 498 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stark-attestations-wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.57" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | wasm-bindgen = "0.2.74" 12 | js-sys = "0.3.57" 13 | air = { package = "giza-air", git = "https://github.com/maxgillett/giza", rev = "ce65e65", version = "0.1", default-features = false } 14 | giza_core = { package = "giza-core", git = "https://github.com/maxgillett/giza", rev = "ce65e65", version = "0.1", default-features = false } 15 | winterfell = { package = "winter-verifier", git = "https://github.com/maxgillett/winterfell", rev = "0aad6a5", version = "0.4", default-features = false } 16 | winter-crypto = { package = "winter-crypto", git = "https://github.com/maxgillett/winterfell", rev = "0aad6a5", version = "0.4", default-features = false } 17 | winter-math = { package = "winter-math", git = "https://github.com/maxgillett/winterfell", rev = "0aad6a5", version = "0.4", default-features = false } 18 | winter-utils = { package = "winter-utils", git = "https://github.com/maxgillett/winterfell", rev = "0aad6a5", version = "0.4", default-features = false } 19 | sha3 = { version = "0.10", default-features = false } 20 | bincode = "1.3.3" 21 | hex = "0.4.3" 22 | serde = { version = "1.0.136", features = ["derive"] } 23 | 24 | [profile.release] 25 | lto = true 26 | opt-level = 's' 27 | 28 | [package.metadata.wasm-pack.profile.release] 29 | wasm-opt = false 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node 2 | MAINTAINER Max Gillett 3 | 4 | RUN apt-get update && \ 5 | apt-get install --no-install-recommends -y \ 6 | ca-certificates curl file \ 7 | build-essential \ 8 | autoconf automake autotools-dev libtool xutils-dev && \ 9 | rm -rf /var/lib/apt/lists/* 10 | 11 | RUN curl https://sh.rustup.rs -sSf | \ 12 | sh -s -- --default-toolchain nightly -y 13 | 14 | ENV PATH=/root/.cargo/bin:$PATH 15 | ENV USER root 16 | WORKDIR /source 17 | 18 | CMD ["bash"] 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | STARK attestations are proofs that the creator held a minimum balance of a token at the end of a given block. Importantly, these tokens may be stored on an anonymous account, but the creator must have the ability to sign messages with that account in order to generate the proof. Proofs are downloaded from IPFS and verified in browser using the Winterfell codebase. 4 | 5 | Try it here: [https://stark-attestations.netlify.app/view/badge/QmUj7cwrY6FqxSHvwN5oAh4yb3xTZu2v3g4yrBFCSQQ3U6](https://stark-attestations.netlify.app/view/badge/QmUj7cwrY6FqxSHvwN5oAh4yb3xTZu2v3g4yrBFCSQQ3U6) 6 | 7 | ## Create an attestation 8 | 9 | Proof generation takes around five minutes with a peak RAM consumption of around 12GB on a Macbook M1. 10 | 11 | You'll need to have the following already installed: 12 | 13 | - Nightly version of Rust 14 | - A Cairo runner 15 | 16 | **Install Giza** 17 | 18 | 1. `git clone https://github.com/maxgillett/giza` 19 | 2. `cd giza` 20 | 3. `cargo install --path cli` 21 | 22 | **Build proof data** 23 | 24 | 1. `git clone https://github.com/maxgillett/stark-attestations` 25 | 2. `cd stark-attestations` 26 | 3. Run `python cli/cli.py` with the provided arguments 27 | 28 | **Generate the proof** 29 | 30 | 1. `cd stark-attestations/programs` 31 | 2. Compile and run the Cairo program: `./compile_and_run.sh` 32 | 3. `giza prove --program=storage_proof.json --trace=trace.bin --memory=memory.bin --output=output.bin --num-outputs=12` 33 | 34 | **Share the proof** 35 | 36 | 1. Upload `output.bin` to IPFS, and note the returned CID (content identifier) 37 | 2. Navigate to `https://stark-attestations.netlify.app/view/badge/{CID}`, replacing your CID 38 | 39 | ## FAQ 40 | 41 | #### Are attestations truly private? 42 | 43 | The current proofs are not perfect zero-knowledge, and it is possible that information about the address that tokens are held or the exact token balance could be extracted under certain conditions. Correcting this does not meaningfully change proof generation time, and will be added shortly. 44 | 45 | #### Is it possible to fake a proof? 46 | 47 | Yes! A couple of AIR constraints are not implemented that could be exploited to generate an incorrect proof (although it would be far from trivial). These constraints will be added in shortly. 48 | 49 | #### Are proofs trustless? 50 | 51 | Yes, the only trust assumption is that the blockhash returned from the Ethereum provider belongs to the claimed block. If you're using your own node, then there is nothing to trust. 52 | 53 | #### Can this be used to anonymously prove membership in a DAO, or an NFT collection? 54 | 55 | Yes, the Cairo program can be easily modified to prove arbitrary conditions for any storage value. 56 | 57 | #### How does it work? 58 | 59 | You can read through the balance checker Cairo program [here](https://github.com/maxgillett/stark-attestations/blob/master/programs/storage_proof.cairo) for information (more documentation to come). To confirm that the proof you are viewing corresponds to this particular program, you should check the computed program hash. The program hash corresponding to `storage_proof.cairo` is `0xcf599536126a617cfcb21610df98c8d08ca668a668b55d3fa9321b89d4504dca` (but don't take my word for it -- you can download the code and check for yourself). 60 | 61 | #### What are STARKs? 62 | 63 | A STARK is a non-interactive proof, and stands for "Scalable Transparent Arguments of Knowledge." You can find more resources about them [here](https://starkware.co/stark/). 64 | 65 | #### What is Cairo? 66 | 67 | Cairo is a programming language written for the Cairo VM. Please see the [Cairo website](https://www.cairo-lang.org/) for more information. 68 | 69 | ## Known issues 70 | 71 | - When first loading the web app there may be an race condition causing the "Connect Wallet" button to be grayed out. This can be resolved by refreshing the page multiple times. 72 | 73 | ## Acknowledgments 74 | 75 | - The L1 state verification library is derived from the [Fossil](https://github.com/OilerNetwork/fossil) library built by [@marcellobardus](https://github.com/marcellobardus) and team at OilerNetwork. 76 | - The STARK prover and verifier is built using the [Winterfell](https://github.com/novifinancial/winterfell) project. 77 | - The Cairo virtual machine and programming language is developed by [Starkware](https://starkware.co/). 78 | -------------------------------------------------------------------------------- /cli/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | import os, json 3 | from dotenv import load_dotenv 4 | from web3 import Web3 5 | from eth_account.messages import encode_defunct 6 | 7 | @click.command() 8 | @click.option('--block_number', default=14987112) 9 | @click.option('--token_address', default='0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72') 10 | @click.option('--token_balance_min', default=150000000000000000000) 11 | @click.option('--public_eth_address') 12 | @click.option('--private_eth_address') 13 | @click.option('--rpc_http') 14 | def create_proof_data( 15 | block_number, 16 | token_address, 17 | token_balance_min, 18 | public_eth_address, 19 | private_eth_address, 20 | rpc_http): 21 | 22 | load_dotenv() 23 | w3 = Web3(Web3.HTTPProvider(rpc_http)) 24 | 25 | # Create storage proof for an ERC20 balance at a particular block number 26 | slot = str(0).rjust(64, '0') 27 | key = private_eth_address[2:].rjust(64, '0').lower() 28 | position = w3.keccak(hexstr=key + slot) 29 | block = w3.eth.get_block(block_number) 30 | proof = w3.eth.get_proof(token_address, [position], block_number) 31 | balance = w3.eth.get_storage_at(token_address, position) 32 | print("Generating proof of balance", Web3.toInt(balance)) 33 | 34 | # Sign a message demonstrating control over the storage slot 35 | # Pad the message with zeros to align 64bit word size in Cairo 36 | state_root = block.stateRoot.hex() 37 | storage_key = proof['storageProof'][0]['key'].hex()[2:] 38 | msg = "000000000000000000000000000000%s%s%s00000000" % ( 39 | public_eth_address[2:], 40 | state_root[2:], 41 | storage_key) 42 | message = encode_defunct(hexstr=msg) 43 | signed_message = w3.eth.account.sign_message(message, private_key=os.environ['PRIVATE_KEY']) 44 | eip191_message = b'\x19' + message.version + message.header + message.body 45 | 46 | print(message.body.hex()[6:]) 47 | print(eip191_message.hex()) 48 | 49 | # Serialize proof to disk 50 | proof_dict = json.loads(Web3.toJSON(proof)) 51 | proof_dict['blockNumber'] = block_number 52 | proof_dict['publicEthAddress'] = int(public_eth_address, 16) 53 | proof_dict['tokenBalanceMin'] = token_balance_min 54 | proof_dict['stateRoot'] = state_root 55 | proof_dict['storageSlot'] = slot 56 | proof_dict["signature"] = { 57 | "message": "0x"+eip191_message.hex(), 58 | "messageHash": signed_message.messageHash.hex(), 59 | "r": signed_message.r, 60 | "s": signed_message.s, 61 | "v": signed_message.v, 62 | } 63 | json.dump(proof_dict, open("proof.json", "w"), indent=4) 64 | 65 | if __name__ == '__main__': 66 | create_proof_data() 67 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | webpack: 4 | image: rust-node 5 | volumes: 6 | - ./:/usr/src/app 7 | - /node_modules 8 | working_dir: /usr/src/app 9 | command: bash -c "curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh && yarn start" 10 | ports: 11 | - "3000:3000" 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-react-app-with-typescript", 3 | "version": "5.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.9.0", 7 | "@emotion/styled": "^11.8.1", 8 | "@mui/icons-material": "^5.8.4", 9 | "@mui/material": "^5.8.3", 10 | "@mui/x-data-grid": "^5.12.0", 11 | "@types/react": "latest", 12 | "@types/react-dom": "latest", 13 | "@web3-react/core": "^8.0.10-beta.0", 14 | "@web3-react/metamask": "^8.0.6-beta.0", 15 | "@web3-react/network": "^8.0.5-beta.0", 16 | "eth-proof": "^2.1.6", 17 | "ipfs-http-client": "^57.0.1", 18 | "moment": "^2.29.3", 19 | "node-polyfill-webpack-plugin": "^1.1.4", 20 | "react": "latest", 21 | "react-dom": "latest", 22 | "react-router-dom": "^6.3.0", 23 | "react-scripts": "latest", 24 | "typescript": "latest", 25 | "web3": "^1.7.3" 26 | }, 27 | "scripts": { 28 | "start": "webpack serve --config webpack.config.ts --open", 29 | "build": "webpack --mode production" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.18.2", 45 | "@babel/plugin-transform-runtime": "^7.18.2", 46 | "@babel/preset-env": "^7.18.2", 47 | "@babel/preset-react": "^7.17.12", 48 | "@babel/preset-typescript": "^7.17.12", 49 | "@babel/runtime": "^7.18.3", 50 | "@wasm-tool/wasm-pack-plugin": "1.6.0", 51 | "babel-loader": "^8.2.5", 52 | "html-webpack-plugin": "^5.5.0", 53 | "text-encoding": "^0.7.0", 54 | "ts-node": "^10.8.1", 55 | "webpack": "^5.73.0", 56 | "webpack-cli": "^4.9.2", 57 | "webpack-dev-server": "^4.9.1" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /programs/.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | proof.json 3 | -------------------------------------------------------------------------------- /programs/compile_and_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cairo-compile storage_proof.cairo \ 4 | --output=storage_proof.json 5 | 6 | cairo-run \ 7 | --program=storage_proof.json \ 8 | --print_info \ 9 | --print_output \ 10 | --layout=all \ 11 | --memory_file=memory.bin \ 12 | --program_input=proof.json \ 13 | --trace_file=trace.bin 14 | -------------------------------------------------------------------------------- /programs/lib/bytes_utils.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.alloc import alloc 2 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 3 | from starkware.cairo.common.math import assert_le, unsigned_div_rem 4 | 5 | struct IntArray: 6 | member elements: felt* 7 | member word_len: felt # number of 64-bit words 8 | member byte_len: felt # total number of bytes 9 | end 10 | 11 | struct ByteArray: 12 | member elements: felt* 13 | member byte_len: felt 14 | end 15 | 16 | # Taken from https://github.com/OilerNetwork/fossil 17 | func swap_endian{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}(input: felt, size: felt) -> (output: felt): 18 | alloc_locals 19 | let (local output : felt*) = alloc() 20 | 21 | # verifies word fits in 64bits 22 | assert_le(input, 2**64 - 1) 23 | 24 | # swapped_bytes = ((word & 0xFF00FF00FF00FF00) >> 8) | ((word & 0x00FF00FF00FF00FF) << 8) 25 | let (left_part, _) = unsigned_div_rem(input, 256) 26 | 27 | assert bitwise_ptr[0].x = left_part 28 | assert bitwise_ptr[0].y = 0x00FF00FF00FF00FF 29 | 30 | assert bitwise_ptr[1].x = input * 256 31 | assert bitwise_ptr[1].y = 0xFF00FF00FF00FF00 32 | 33 | let swapped_bytes = bitwise_ptr[0].x_and_y + bitwise_ptr[1].x_and_y 34 | 35 | # swapped_2byte_pair = ((swapped_bytes & 0xFFFF0000FFFF0000) >> 16) | ((swapped_bytes & 0x0000FFFF0000FFFF) << 16) 36 | let (left_part2, _) = unsigned_div_rem(swapped_bytes, 2**16) 37 | 38 | assert bitwise_ptr[2].x = left_part2 39 | assert bitwise_ptr[2].y = 0x0000FFFF0000FFFF 40 | 41 | assert bitwise_ptr[3].x = swapped_bytes * 2**16 42 | assert bitwise_ptr[3].y = 0xFFFF0000FFFF0000 43 | 44 | let swapped_2bytes = bitwise_ptr[2].x_and_y + bitwise_ptr[3].x_and_y 45 | 46 | # swapped_4byte_pair = (swapped_2byte_pair >> 32) | ((swapped_2byte_pair << 32) % 2**64) 47 | let (left_part4, _) = unsigned_div_rem(swapped_2bytes, 2**32) 48 | 49 | assert bitwise_ptr[4].x = swapped_2bytes * 2**32 50 | assert bitwise_ptr[4].y = 0xFFFFFFFF00000000 51 | 52 | let swapped_4bytes = left_part4 + bitwise_ptr[4].x_and_y 53 | 54 | let bitwise_ptr = bitwise_ptr + 5 * BitwiseBuiltin.SIZE 55 | 56 | # Some Shiva-inspired code here 57 | let (local shift) = pow(2, ((8 - size) * 8)) 58 | 59 | if size == 8: 60 | return (swapped_4bytes) 61 | else: 62 | let (shifted_4bytes, _) = unsigned_div_rem(swapped_4bytes, shift) 63 | return (shifted_4bytes) 64 | end 65 | end 66 | 67 | # Taken from https://github.com/OilerNetwork/fossil 68 | func pow{range_check_ptr}(base: felt, p: felt) -> (res: felt): 69 | if p == 0: 70 | return (1) 71 | end 72 | let (accumulator) = pow(base=base, p=p-1) 73 | return (base * accumulator) 74 | end 75 | 76 | func bitshift_right{range_check_ptr}(word: felt, num_bits: felt) -> (shifted: felt): 77 | assert_le(word, 2**64 - 1) # verifies word fits in 64bits 78 | assert_le(num_bits, 64) # verifies shifted bits are not above 64 79 | 80 | let (divider) = pow(2, num_bits) 81 | let (left_part, _) = unsigned_div_rem(word, divider) 82 | return (left_part) 83 | end 84 | -------------------------------------------------------------------------------- /programs/lib/keccak.cairo: -------------------------------------------------------------------------------- 1 | from lib.packed_keccak import BLOCK_SIZE, packed_keccak_func 2 | from starkware.cairo.common.alloc import alloc 3 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 4 | from starkware.cairo.common.math import assert_nn_le, unsigned_div_rem 5 | from starkware.cairo.common.memset import memset 6 | from starkware.cairo.common.pow import pow 7 | 8 | const KECCAK_STATE_SIZE_FELTS = 25 9 | 10 | # Computes the keccak of 'input'. Inputs of up to 127 bytes are supported. 11 | # To use this function, split the input into (up to) 16 words of 64 bits (little endian). 12 | # For example, to compute keccak('Hello world!'), use: 13 | # input = [8031924123371070792, 560229490] 14 | # where: 15 | # 8031924123371070792 == int.from_bytes(b'Hello wo', 'little') 16 | # 560229490 == int.from_bytes(b'rld!', 'little') 17 | # 18 | # output is an array of 4 64-bit words (little endian). 19 | # 20 | # Assumption: n_bytes <= 127. 21 | # 22 | # Note: You must call finalize_keccak() at the end of the program. Otherwise, this function 23 | # is not sound and a malicious prover may return a wrong result. 24 | # Note: the interface of this function may change in the future. 25 | func keccak{range_check_ptr, keccak_ptr : felt*}(input : felt*, n_bytes : felt) -> (output : felt*): 26 | assert_nn_le(n_bytes, 127) 27 | let keccak_ptr_start = keccak_ptr 28 | _keccak_input(input=input, n_bytes=n_bytes, n_words=16) 29 | assert keccak_ptr[0] = 2 ** 63 30 | assert keccak_ptr[1] = 0 31 | assert keccak_ptr[2] = 0 32 | assert keccak_ptr[3] = 0 33 | assert keccak_ptr[4] = 0 34 | assert keccak_ptr[5] = 0 35 | assert keccak_ptr[6] = 0 36 | assert keccak_ptr[7] = 0 37 | assert keccak_ptr[8] = 0 38 | let keccak_ptr = keccak_ptr + 9 39 | 40 | let output = keccak_ptr 41 | %{ 42 | from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func 43 | _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) 44 | assert 0 <= _keccak_state_size_felts < 100 45 | output_values = keccak_func(memory.get_range( 46 | ids.keccak_ptr_start, _keccak_state_size_felts)) 47 | segments.write_arg(ids.output, output_values) 48 | %} 49 | let keccak_ptr = keccak_ptr + KECCAK_STATE_SIZE_FELTS 50 | return (output) 51 | end 52 | 53 | func _keccak_input{range_check_ptr, keccak_ptr : felt*}( 54 | input : felt*, n_bytes : felt, n_words : felt): 55 | alloc_locals 56 | 57 | local full_word 58 | %{ ids.full_word = int(ids.n_bytes >= 8) %} 59 | 60 | if full_word != 0: 61 | assert keccak_ptr[0] = input[0] 62 | let keccak_ptr = keccak_ptr + 1 63 | return _keccak_input(input=input + 1, n_bytes=n_bytes - 8, n_words=n_words - 1) 64 | else: 65 | if n_bytes == 0: 66 | assert keccak_ptr[0] = 1 67 | memset(dst=keccak_ptr + 1, value=0, n=n_words - 1) 68 | let keccak_ptr = keccak_ptr + n_words 69 | return () 70 | end 71 | 72 | assert_nn_le(n_bytes, 7) 73 | let (padding) = pow(256, n_bytes) 74 | local range_check_ptr = range_check_ptr 75 | 76 | assert keccak_ptr[0] = input[0] + padding 77 | 78 | memset(dst=keccak_ptr + 1, value=0, n=n_words - 1) 79 | let keccak_ptr = keccak_ptr + n_words 80 | return () 81 | end 82 | end 83 | 84 | # Handles n blocks of BLOCK_SIZE keccak instances. 85 | func _finalize_keccak_inner{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}( 86 | keccak_ptr : felt*, n : felt): 87 | if n == 0: 88 | return () 89 | end 90 | 91 | alloc_locals 92 | 93 | local MAX_VALUE = 2 ** 64 - 1 94 | 95 | let keccak_ptr_start = keccak_ptr 96 | 97 | let (local inputs_start : felt*) = alloc() 98 | 99 | # Handle inputs. 100 | 101 | tempvar inputs = inputs_start 102 | tempvar keccak_ptr = keccak_ptr 103 | tempvar range_check_ptr = range_check_ptr 104 | tempvar m = 25 105 | 106 | input_loop: 107 | tempvar x0 = keccak_ptr[0] 108 | assert [range_check_ptr] = x0 109 | assert [range_check_ptr + 1] = MAX_VALUE - x0 110 | tempvar x1 = keccak_ptr[50] 111 | assert [range_check_ptr + 2] = x1 112 | assert [range_check_ptr + 3] = MAX_VALUE - x1 113 | tempvar x2 = keccak_ptr[100] 114 | assert [range_check_ptr + 4] = x2 115 | assert [range_check_ptr + 5] = MAX_VALUE - x2 116 | assert inputs[0] = x0 + 2 ** 64 * x1 + 2 ** 128 * x2 117 | 118 | tempvar inputs = inputs + 1 119 | tempvar keccak_ptr = keccak_ptr + 1 120 | tempvar range_check_ptr = range_check_ptr + 6 121 | tempvar m = m - 1 122 | jmp input_loop if m != 0 123 | 124 | # Run keccak on the 3 instances. 125 | 126 | let (outputs) = packed_keccak_func(inputs_start) 127 | local bitwise_ptr : BitwiseBuiltin* = bitwise_ptr 128 | 129 | # Handle outputs. 130 | 131 | tempvar outputs = outputs 132 | tempvar keccak_ptr = keccak_ptr 133 | tempvar range_check_ptr = range_check_ptr 134 | tempvar m = 25 135 | 136 | output_loop: 137 | tempvar x0 = keccak_ptr[0] 138 | assert [range_check_ptr] = x0 139 | assert [range_check_ptr + 1] = MAX_VALUE - x0 140 | tempvar x1 = keccak_ptr[50] 141 | assert [range_check_ptr + 2] = x1 142 | assert [range_check_ptr + 3] = MAX_VALUE - x1 143 | tempvar x2 = keccak_ptr[100] 144 | assert [range_check_ptr + 4] = x2 145 | assert [range_check_ptr + 5] = MAX_VALUE - x2 146 | assert outputs[0] = x0 + 2 ** 64 * x1 + 2 ** 128 * x2 147 | 148 | tempvar outputs = outputs + 1 149 | tempvar keccak_ptr = keccak_ptr + 1 150 | tempvar range_check_ptr = range_check_ptr + 6 151 | tempvar m = m - 1 152 | jmp output_loop if m != 0 153 | 154 | return _finalize_keccak_inner(keccak_ptr=keccak_ptr_start + 150, n=n - 1) 155 | end 156 | 157 | # Verifies that the results of keccak() are valid. 158 | func finalize_keccak{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}( 159 | keccak_ptr_start : felt*, keccak_ptr_end : felt*): 160 | alloc_locals 161 | 162 | tempvar n = (keccak_ptr_end - keccak_ptr_start) / (2 * KECCAK_STATE_SIZE_FELTS) 163 | if n == 0: 164 | return () 165 | end 166 | 167 | %{ 168 | # Add dummy pairs of input and output. 169 | _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) 170 | _block_size = int(ids.BLOCK_SIZE) 171 | assert 0 <= _keccak_state_size_felts < 100 172 | assert 0 <= _block_size < 1000 173 | inp = [0] * _keccak_state_size_felts 174 | padding = (inp + keccak_func(inp)) * _block_size 175 | segments.write_arg(ids.keccak_ptr_end, padding) 176 | %} 177 | 178 | # Compute the amount of blocks (rounded up). 179 | let (local q, r) = unsigned_div_rem(n + BLOCK_SIZE - 1, BLOCK_SIZE) 180 | _finalize_keccak_inner(keccak_ptr_start, n=q) 181 | return () 182 | end 183 | -------------------------------------------------------------------------------- /programs/lib/packed_keccak.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.alloc import alloc 2 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 3 | from starkware.cairo.common.registers import get_fp_and_pc 4 | 5 | const ALL_ONES = 2 ** 251 - 1 6 | const BLOCK_SIZE = 3 7 | const SHIFTS = 1 + 2 ** 64 + 2 ** 128 8 | 9 | func keccak_round{bitwise_ptr : BitwiseBuiltin*}(values : felt*, rc : felt) -> (values_b : felt*): 10 | alloc_locals 11 | 12 | ############################################################################################## 13 | # Compute: c[x] = a[0][x] ^ a[1][x] ^ a[2][x] ^ a[3][x] ^ a[4][x]. # 14 | ############################################################################################## 15 | 16 | let values_start = values 17 | 18 | assert bitwise_ptr[0].x = values[0] 19 | assert bitwise_ptr[0].y = values[5] 20 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_xor_y 21 | assert bitwise_ptr[1].y = values[10] 22 | assert bitwise_ptr[2].x = bitwise_ptr[1].x_xor_y 23 | assert bitwise_ptr[2].y = values[15] 24 | assert bitwise_ptr[3].x = bitwise_ptr[2].x_xor_y 25 | assert bitwise_ptr[3].y = values[20] 26 | tempvar c0 = bitwise_ptr[3].x_xor_y 27 | let values = values + 1 28 | let bitwise_ptr = bitwise_ptr + 4 * BitwiseBuiltin.SIZE 29 | 30 | assert bitwise_ptr[0].x = values[0] 31 | assert bitwise_ptr[0].y = values[5] 32 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_xor_y 33 | assert bitwise_ptr[1].y = values[10] 34 | assert bitwise_ptr[2].x = bitwise_ptr[1].x_xor_y 35 | assert bitwise_ptr[2].y = values[15] 36 | assert bitwise_ptr[3].x = bitwise_ptr[2].x_xor_y 37 | assert bitwise_ptr[3].y = values[20] 38 | tempvar c1 = bitwise_ptr[3].x_xor_y 39 | let values = values + 1 40 | let bitwise_ptr = bitwise_ptr + 4 * BitwiseBuiltin.SIZE 41 | 42 | assert bitwise_ptr[0].x = values[0] 43 | assert bitwise_ptr[0].y = values[5] 44 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_xor_y 45 | assert bitwise_ptr[1].y = values[10] 46 | assert bitwise_ptr[2].x = bitwise_ptr[1].x_xor_y 47 | assert bitwise_ptr[2].y = values[15] 48 | assert bitwise_ptr[3].x = bitwise_ptr[2].x_xor_y 49 | assert bitwise_ptr[3].y = values[20] 50 | tempvar c2 = bitwise_ptr[3].x_xor_y 51 | let values = values + 1 52 | let bitwise_ptr = bitwise_ptr + 4 * BitwiseBuiltin.SIZE 53 | 54 | assert bitwise_ptr[0].x = values[0] 55 | assert bitwise_ptr[0].y = values[5] 56 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_xor_y 57 | assert bitwise_ptr[1].y = values[10] 58 | assert bitwise_ptr[2].x = bitwise_ptr[1].x_xor_y 59 | assert bitwise_ptr[2].y = values[15] 60 | assert bitwise_ptr[3].x = bitwise_ptr[2].x_xor_y 61 | assert bitwise_ptr[3].y = values[20] 62 | tempvar c3 = bitwise_ptr[3].x_xor_y 63 | let values = values + 1 64 | let bitwise_ptr = bitwise_ptr + 4 * BitwiseBuiltin.SIZE 65 | 66 | assert bitwise_ptr[0].x = values[0] 67 | assert bitwise_ptr[0].y = values[5] 68 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_xor_y 69 | assert bitwise_ptr[1].y = values[10] 70 | assert bitwise_ptr[2].x = bitwise_ptr[1].x_xor_y 71 | assert bitwise_ptr[2].y = values[15] 72 | assert bitwise_ptr[3].x = bitwise_ptr[2].x_xor_y 73 | assert bitwise_ptr[3].y = values[20] 74 | tempvar c4 = bitwise_ptr[3].x_xor_y 75 | let values = values + 1 76 | let bitwise_ptr = bitwise_ptr + 4 * BitwiseBuiltin.SIZE 77 | 78 | ############################################################################################## 79 | # Compute: d[x] = c[(x - 1) % 5] ^ rot_left(c[(x + 1) % 5], 1). # 80 | ############################################################################################## 81 | 82 | # Saving constants as local variables is more efficient in some instructions. 83 | local mask = 0x800000000000000080000000000000008000000000000000 84 | 85 | let x = c1 86 | let y = c4 87 | assert bitwise_ptr[0].x = x 88 | assert bitwise_ptr[0].y = mask 89 | tempvar x0 = bitwise_ptr[0].x_and_y 90 | let rotx = 2 * x + (1 / 2 ** 63 - 2) * x0 91 | assert bitwise_ptr[1].x = rotx 92 | assert bitwise_ptr[1].y = y 93 | tempvar d0 = bitwise_ptr[1].x_xor_y 94 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 95 | 96 | let x = c2 97 | let y = c0 98 | assert bitwise_ptr[0].x = x 99 | assert bitwise_ptr[0].y = mask 100 | tempvar x0 = bitwise_ptr[0].x_and_y 101 | let rotx = 2 * x + (1 / 2 ** 63 - 2) * x0 102 | assert bitwise_ptr[1].x = rotx 103 | assert bitwise_ptr[1].y = y 104 | tempvar d1 = bitwise_ptr[1].x_xor_y 105 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 106 | 107 | let x = c3 108 | let y = c1 109 | assert bitwise_ptr[0].x = x 110 | assert bitwise_ptr[0].y = mask 111 | tempvar x0 = bitwise_ptr[0].x_and_y 112 | let rotx = 2 * x + (1 / 2 ** 63 - 2) * x0 113 | assert bitwise_ptr[1].x = rotx 114 | assert bitwise_ptr[1].y = y 115 | tempvar d2 = bitwise_ptr[1].x_xor_y 116 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 117 | 118 | let x = c4 119 | let y = c2 120 | assert bitwise_ptr[0].x = x 121 | assert bitwise_ptr[0].y = mask 122 | tempvar x0 = bitwise_ptr[0].x_and_y 123 | let rotx = 2 * x + (1 / 2 ** 63 - 2) * x0 124 | assert bitwise_ptr[1].x = rotx 125 | assert bitwise_ptr[1].y = y 126 | tempvar d3 = bitwise_ptr[1].x_xor_y 127 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 128 | 129 | let x = c0 130 | let y = c3 131 | assert bitwise_ptr[0].x = x 132 | assert bitwise_ptr[0].y = mask 133 | tempvar x0 = bitwise_ptr[0].x_and_y 134 | let rotx = 2 * x + (1 / 2 ** 63 - 2) * x0 135 | assert bitwise_ptr[1].x = rotx 136 | assert bitwise_ptr[1].y = y 137 | tempvar d4 = bitwise_ptr[1].x_xor_y 138 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 139 | 140 | ############################################################################################## 141 | # Compute: b[(2 * x + 3 * y) % 5][y] = rot_left([a[y][x] ^ d[x], OFFSETS[x][y]) # 142 | ############################################################################################## 143 | 144 | let values = values_start 145 | 146 | assert bitwise_ptr[0].x = values[0] 147 | assert bitwise_ptr[0].y = d0 148 | tempvar x = bitwise_ptr[0].x_xor_y 149 | assert bitwise_ptr[1].x = x 150 | assert bitwise_ptr[1].y = 0x0 151 | tempvar b0 = 2 ** 0 * x + (1 / 2 ** 64 - 2 ** 0) * bitwise_ptr[1].x_and_y 152 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 153 | 154 | assert bitwise_ptr[0].x = values[5] 155 | assert bitwise_ptr[0].y = d0 156 | tempvar x = bitwise_ptr[0].x_xor_y 157 | assert bitwise_ptr[1].x = x 158 | assert bitwise_ptr[1].y = 0xfffffffff0000000fffffffff0000000fffffffff0000000 159 | tempvar b16 = 2 ** 36 * x + (1 / 2 ** 28 - 2 ** 36) * bitwise_ptr[1].x_and_y 160 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 161 | 162 | assert bitwise_ptr[0].x = values[10] 163 | assert bitwise_ptr[0].y = d0 164 | tempvar x = bitwise_ptr[0].x_xor_y 165 | assert bitwise_ptr[1].x = x 166 | assert bitwise_ptr[1].y = 0xe000000000000000e000000000000000e000000000000000 167 | tempvar b7 = 2 ** 3 * x + (1 / 2 ** 61 - 2 ** 3) * bitwise_ptr[1].x_and_y 168 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 169 | 170 | assert bitwise_ptr[0].x = values[15] 171 | assert bitwise_ptr[0].y = d0 172 | tempvar x = bitwise_ptr[0].x_xor_y 173 | assert bitwise_ptr[1].x = x 174 | assert bitwise_ptr[1].y = 0xffffffffff800000ffffffffff800000ffffffffff800000 175 | tempvar b23 = 2 ** 41 * x + (1 / 2 ** 23 - 2 ** 41) * bitwise_ptr[1].x_and_y 176 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 177 | 178 | assert bitwise_ptr[0].x = values[20] 179 | assert bitwise_ptr[0].y = d0 180 | tempvar x = bitwise_ptr[0].x_xor_y 181 | assert bitwise_ptr[1].x = x 182 | assert bitwise_ptr[1].y = 0xffffc00000000000ffffc00000000000ffffc00000000000 183 | tempvar b14 = 2 ** 18 * x + (1 / 2 ** 46 - 2 ** 18) * bitwise_ptr[1].x_and_y 184 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 185 | 186 | assert bitwise_ptr[0].x = values[1] 187 | assert bitwise_ptr[0].y = d1 188 | tempvar x = bitwise_ptr[0].x_xor_y 189 | assert bitwise_ptr[1].x = x 190 | assert bitwise_ptr[1].y = 0x800000000000000080000000000000008000000000000000 191 | tempvar b10 = 2 ** 1 * x + (1 / 2 ** 63 - 2 ** 1) * bitwise_ptr[1].x_and_y 192 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 193 | 194 | assert bitwise_ptr[0].x = values[6] 195 | assert bitwise_ptr[0].y = d1 196 | tempvar x = bitwise_ptr[0].x_xor_y 197 | assert bitwise_ptr[1].x = x 198 | assert bitwise_ptr[1].y = 0xfffffffffff00000fffffffffff00000fffffffffff00000 199 | tempvar b1 = 2 ** 44 * x + (1 / 2 ** 20 - 2 ** 44) * bitwise_ptr[1].x_and_y 200 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 201 | 202 | assert bitwise_ptr[0].x = values[11] 203 | assert bitwise_ptr[0].y = d1 204 | tempvar x = bitwise_ptr[0].x_xor_y 205 | assert bitwise_ptr[1].x = x 206 | assert bitwise_ptr[1].y = 0xffc0000000000000ffc0000000000000ffc0000000000000 207 | tempvar b17 = 2 ** 10 * x + (1 / 2 ** 54 - 2 ** 10) * bitwise_ptr[1].x_and_y 208 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 209 | 210 | assert bitwise_ptr[0].x = values[16] 211 | assert bitwise_ptr[0].y = d1 212 | tempvar x = bitwise_ptr[0].x_xor_y 213 | assert bitwise_ptr[1].x = x 214 | assert bitwise_ptr[1].y = 0xfffffffffff80000fffffffffff80000fffffffffff80000 215 | tempvar b8 = 2 ** 45 * x + (1 / 2 ** 19 - 2 ** 45) * bitwise_ptr[1].x_and_y 216 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 217 | 218 | assert bitwise_ptr[0].x = values[21] 219 | assert bitwise_ptr[0].y = d1 220 | tempvar x = bitwise_ptr[0].x_xor_y 221 | assert bitwise_ptr[1].x = x 222 | assert bitwise_ptr[1].y = 0xc000000000000000c000000000000000c000000000000000 223 | tempvar b24 = 2 ** 2 * x + (1 / 2 ** 62 - 2 ** 2) * bitwise_ptr[1].x_and_y 224 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 225 | 226 | assert bitwise_ptr[0].x = values[2] 227 | assert bitwise_ptr[0].y = d2 228 | tempvar x = bitwise_ptr[0].x_xor_y 229 | assert bitwise_ptr[1].x = x 230 | assert bitwise_ptr[1].y = 0xfffffffffffffffcfffffffffffffffcfffffffffffffffc 231 | tempvar b20 = 2 ** 62 * x + (1 / 2 ** 2 - 2 ** 62) * bitwise_ptr[1].x_and_y 232 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 233 | 234 | assert bitwise_ptr[0].x = values[7] 235 | assert bitwise_ptr[0].y = d2 236 | tempvar x = bitwise_ptr[0].x_xor_y 237 | assert bitwise_ptr[1].x = x 238 | assert bitwise_ptr[1].y = 0xfc00000000000000fc00000000000000fc00000000000000 239 | tempvar b11 = 2 ** 6 * x + (1 / 2 ** 58 - 2 ** 6) * bitwise_ptr[1].x_and_y 240 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 241 | 242 | assert bitwise_ptr[0].x = values[12] 243 | assert bitwise_ptr[0].y = d2 244 | tempvar x = bitwise_ptr[0].x_xor_y 245 | assert bitwise_ptr[1].x = x 246 | assert bitwise_ptr[1].y = 0xffffffffffe00000ffffffffffe00000ffffffffffe00000 247 | tempvar b2 = 2 ** 43 * x + (1 / 2 ** 21 - 2 ** 43) * bitwise_ptr[1].x_and_y 248 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 249 | 250 | assert bitwise_ptr[0].x = values[17] 251 | assert bitwise_ptr[0].y = d2 252 | tempvar x = bitwise_ptr[0].x_xor_y 253 | assert bitwise_ptr[1].x = x 254 | assert bitwise_ptr[1].y = 0xfffe000000000000fffe000000000000fffe000000000000 255 | tempvar b18 = 2 ** 15 * x + (1 / 2 ** 49 - 2 ** 15) * bitwise_ptr[1].x_and_y 256 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 257 | 258 | assert bitwise_ptr[0].x = values[22] 259 | assert bitwise_ptr[0].y = d2 260 | tempvar x = bitwise_ptr[0].x_xor_y 261 | assert bitwise_ptr[1].x = x 262 | assert bitwise_ptr[1].y = 0xfffffffffffffff8fffffffffffffff8fffffffffffffff8 263 | tempvar b9 = 2 ** 61 * x + (1 / 2 ** 3 - 2 ** 61) * bitwise_ptr[1].x_and_y 264 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 265 | 266 | assert bitwise_ptr[0].x = values[3] 267 | assert bitwise_ptr[0].y = d3 268 | tempvar x = bitwise_ptr[0].x_xor_y 269 | assert bitwise_ptr[1].x = x 270 | assert bitwise_ptr[1].y = 0xfffffff000000000fffffff000000000fffffff000000000 271 | tempvar b5 = 2 ** 28 * x + (1 / 2 ** 36 - 2 ** 28) * bitwise_ptr[1].x_and_y 272 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 273 | 274 | assert bitwise_ptr[0].x = values[8] 275 | assert bitwise_ptr[0].y = d3 276 | tempvar x = bitwise_ptr[0].x_xor_y 277 | assert bitwise_ptr[1].x = x 278 | assert bitwise_ptr[1].y = 0xfffffffffffffe00fffffffffffffe00fffffffffffffe00 279 | tempvar b21 = 2 ** 55 * x + (1 / 2 ** 9 - 2 ** 55) * bitwise_ptr[1].x_and_y 280 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 281 | 282 | assert bitwise_ptr[0].x = values[13] 283 | assert bitwise_ptr[0].y = d3 284 | tempvar x = bitwise_ptr[0].x_xor_y 285 | assert bitwise_ptr[1].x = x 286 | assert bitwise_ptr[1].y = 0xffffff8000000000ffffff8000000000ffffff8000000000 287 | tempvar b12 = 2 ** 25 * x + (1 / 2 ** 39 - 2 ** 25) * bitwise_ptr[1].x_and_y 288 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 289 | 290 | assert bitwise_ptr[0].x = values[18] 291 | assert bitwise_ptr[0].y = d3 292 | tempvar x = bitwise_ptr[0].x_xor_y 293 | assert bitwise_ptr[1].x = x 294 | assert bitwise_ptr[1].y = 0xfffff80000000000fffff80000000000fffff80000000000 295 | tempvar b3 = 2 ** 21 * x + (1 / 2 ** 43 - 2 ** 21) * bitwise_ptr[1].x_and_y 296 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 297 | 298 | assert bitwise_ptr[0].x = values[23] 299 | assert bitwise_ptr[0].y = d3 300 | tempvar x = bitwise_ptr[0].x_xor_y 301 | assert bitwise_ptr[1].x = x 302 | assert bitwise_ptr[1].y = 0xffffffffffffff00ffffffffffffff00ffffffffffffff00 303 | tempvar b19 = 2 ** 56 * x + (1 / 2 ** 8 - 2 ** 56) * bitwise_ptr[1].x_and_y 304 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 305 | 306 | assert bitwise_ptr[0].x = values[4] 307 | assert bitwise_ptr[0].y = d4 308 | tempvar x = bitwise_ptr[0].x_xor_y 309 | assert bitwise_ptr[1].x = x 310 | assert bitwise_ptr[1].y = 0xffffffe000000000ffffffe000000000ffffffe000000000 311 | tempvar b15 = 2 ** 27 * x + (1 / 2 ** 37 - 2 ** 27) * bitwise_ptr[1].x_and_y 312 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 313 | 314 | assert bitwise_ptr[0].x = values[9] 315 | assert bitwise_ptr[0].y = d4 316 | tempvar x = bitwise_ptr[0].x_xor_y 317 | assert bitwise_ptr[1].x = x 318 | assert bitwise_ptr[1].y = 0xfffff00000000000fffff00000000000fffff00000000000 319 | tempvar b6 = 2 ** 20 * x + (1 / 2 ** 44 - 2 ** 20) * bitwise_ptr[1].x_and_y 320 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 321 | 322 | assert bitwise_ptr[0].x = values[14] 323 | assert bitwise_ptr[0].y = d4 324 | tempvar x = bitwise_ptr[0].x_xor_y 325 | assert bitwise_ptr[1].x = x 326 | assert bitwise_ptr[1].y = 0xfffffffffe000000fffffffffe000000fffffffffe000000 327 | tempvar b22 = 2 ** 39 * x + (1 / 2 ** 25 - 2 ** 39) * bitwise_ptr[1].x_and_y 328 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 329 | 330 | assert bitwise_ptr[0].x = values[19] 331 | assert bitwise_ptr[0].y = d4 332 | tempvar x = bitwise_ptr[0].x_xor_y 333 | assert bitwise_ptr[1].x = x 334 | assert bitwise_ptr[1].y = 0xff00000000000000ff00000000000000ff00000000000000 335 | tempvar b13 = 2 ** 8 * x + (1 / 2 ** 56 - 2 ** 8) * bitwise_ptr[1].x_and_y 336 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 337 | 338 | assert bitwise_ptr[0].x = values[24] 339 | assert bitwise_ptr[0].y = d4 340 | tempvar x = bitwise_ptr[0].x_xor_y 341 | assert bitwise_ptr[1].x = x 342 | assert bitwise_ptr[1].y = 0xfffc000000000000fffc000000000000fffc000000000000 343 | tempvar b4 = 2 ** 14 * x + (1 / 2 ** 50 - 2 ** 14) * bitwise_ptr[1].x_and_y 344 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 345 | 346 | ############################################################################################## 347 | # Compute: a[y][x] = [[a[y][x] ^ ((~a[y][(x + 1) % 5]) & a[y][(x + 2) % 5]) # 348 | ############################################################################################## 349 | 350 | let (local output : felt*) = alloc() 351 | 352 | assert bitwise_ptr[0].x = ALL_ONES - b1 353 | assert bitwise_ptr[0].y = b2 354 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 355 | assert bitwise_ptr[1].y = b0 356 | assert bitwise_ptr[2].x = bitwise_ptr[1].x_xor_y 357 | assert bitwise_ptr[2].y = rc 358 | assert output[0] = bitwise_ptr[2].x_xor_y 359 | let bitwise_ptr = bitwise_ptr + 3 * BitwiseBuiltin.SIZE 360 | 361 | assert bitwise_ptr[0].x = ALL_ONES - b6 362 | assert bitwise_ptr[0].y = b7 363 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 364 | assert bitwise_ptr[1].y = b5 365 | assert output[5] = bitwise_ptr[1].x_xor_y 366 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 367 | 368 | assert bitwise_ptr[0].x = ALL_ONES - b11 369 | assert bitwise_ptr[0].y = b12 370 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 371 | assert bitwise_ptr[1].y = b10 372 | assert output[10] = bitwise_ptr[1].x_xor_y 373 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 374 | 375 | assert bitwise_ptr[0].x = ALL_ONES - b16 376 | assert bitwise_ptr[0].y = b17 377 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 378 | assert bitwise_ptr[1].y = b15 379 | assert output[15] = bitwise_ptr[1].x_xor_y 380 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 381 | 382 | assert bitwise_ptr[0].x = ALL_ONES - b21 383 | assert bitwise_ptr[0].y = b22 384 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 385 | assert bitwise_ptr[1].y = b20 386 | assert output[20] = bitwise_ptr[1].x_xor_y 387 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 388 | 389 | assert bitwise_ptr[0].x = ALL_ONES - b2 390 | assert bitwise_ptr[0].y = b3 391 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 392 | assert bitwise_ptr[1].y = b1 393 | assert output[1] = bitwise_ptr[1].x_xor_y 394 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 395 | 396 | assert bitwise_ptr[0].x = ALL_ONES - b7 397 | assert bitwise_ptr[0].y = b8 398 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 399 | assert bitwise_ptr[1].y = b6 400 | assert output[6] = bitwise_ptr[1].x_xor_y 401 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 402 | 403 | assert bitwise_ptr[0].x = ALL_ONES - b12 404 | assert bitwise_ptr[0].y = b13 405 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 406 | assert bitwise_ptr[1].y = b11 407 | assert output[11] = bitwise_ptr[1].x_xor_y 408 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 409 | 410 | assert bitwise_ptr[0].x = ALL_ONES - b17 411 | assert bitwise_ptr[0].y = b18 412 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 413 | assert bitwise_ptr[1].y = b16 414 | assert output[16] = bitwise_ptr[1].x_xor_y 415 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 416 | 417 | assert bitwise_ptr[0].x = ALL_ONES - b22 418 | assert bitwise_ptr[0].y = b23 419 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 420 | assert bitwise_ptr[1].y = b21 421 | assert output[21] = bitwise_ptr[1].x_xor_y 422 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 423 | 424 | assert bitwise_ptr[0].x = ALL_ONES - b3 425 | assert bitwise_ptr[0].y = b4 426 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 427 | assert bitwise_ptr[1].y = b2 428 | assert output[2] = bitwise_ptr[1].x_xor_y 429 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 430 | 431 | assert bitwise_ptr[0].x = ALL_ONES - b8 432 | assert bitwise_ptr[0].y = b9 433 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 434 | assert bitwise_ptr[1].y = b7 435 | assert output[7] = bitwise_ptr[1].x_xor_y 436 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 437 | 438 | assert bitwise_ptr[0].x = ALL_ONES - b13 439 | assert bitwise_ptr[0].y = b14 440 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 441 | assert bitwise_ptr[1].y = b12 442 | assert output[12] = bitwise_ptr[1].x_xor_y 443 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 444 | 445 | assert bitwise_ptr[0].x = ALL_ONES - b18 446 | assert bitwise_ptr[0].y = b19 447 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 448 | assert bitwise_ptr[1].y = b17 449 | assert output[17] = bitwise_ptr[1].x_xor_y 450 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 451 | 452 | assert bitwise_ptr[0].x = ALL_ONES - b23 453 | assert bitwise_ptr[0].y = b24 454 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 455 | assert bitwise_ptr[1].y = b22 456 | assert output[22] = bitwise_ptr[1].x_xor_y 457 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 458 | 459 | assert bitwise_ptr[0].x = ALL_ONES - b4 460 | assert bitwise_ptr[0].y = b0 461 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 462 | assert bitwise_ptr[1].y = b3 463 | assert output[3] = bitwise_ptr[1].x_xor_y 464 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 465 | 466 | assert bitwise_ptr[0].x = ALL_ONES - b9 467 | assert bitwise_ptr[0].y = b5 468 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 469 | assert bitwise_ptr[1].y = b8 470 | assert output[8] = bitwise_ptr[1].x_xor_y 471 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 472 | 473 | assert bitwise_ptr[0].x = ALL_ONES - b14 474 | assert bitwise_ptr[0].y = b10 475 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 476 | assert bitwise_ptr[1].y = b13 477 | assert output[13] = bitwise_ptr[1].x_xor_y 478 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 479 | 480 | assert bitwise_ptr[0].x = ALL_ONES - b19 481 | assert bitwise_ptr[0].y = b15 482 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 483 | assert bitwise_ptr[1].y = b18 484 | assert output[18] = bitwise_ptr[1].x_xor_y 485 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 486 | 487 | assert bitwise_ptr[0].x = ALL_ONES - b24 488 | assert bitwise_ptr[0].y = b20 489 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 490 | assert bitwise_ptr[1].y = b23 491 | assert output[23] = bitwise_ptr[1].x_xor_y 492 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 493 | 494 | assert bitwise_ptr[0].x = ALL_ONES - b0 495 | assert bitwise_ptr[0].y = b1 496 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 497 | assert bitwise_ptr[1].y = b4 498 | assert output[4] = bitwise_ptr[1].x_xor_y 499 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 500 | 501 | assert bitwise_ptr[0].x = ALL_ONES - b5 502 | assert bitwise_ptr[0].y = b6 503 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 504 | assert bitwise_ptr[1].y = b9 505 | assert output[9] = bitwise_ptr[1].x_xor_y 506 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 507 | 508 | assert bitwise_ptr[0].x = ALL_ONES - b10 509 | assert bitwise_ptr[0].y = b11 510 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 511 | assert bitwise_ptr[1].y = b14 512 | assert output[14] = bitwise_ptr[1].x_xor_y 513 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 514 | 515 | assert bitwise_ptr[0].x = ALL_ONES - b15 516 | assert bitwise_ptr[0].y = b16 517 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 518 | assert bitwise_ptr[1].y = b19 519 | assert output[19] = bitwise_ptr[1].x_xor_y 520 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 521 | 522 | assert bitwise_ptr[0].x = ALL_ONES - b20 523 | assert bitwise_ptr[0].y = b21 524 | assert bitwise_ptr[1].x = bitwise_ptr[0].x_and_y 525 | assert bitwise_ptr[1].y = b24 526 | assert output[24] = bitwise_ptr[1].x_xor_y 527 | let bitwise_ptr = bitwise_ptr + 2 * BitwiseBuiltin.SIZE 528 | 529 | return (output) 530 | end 531 | 532 | func packed_keccak_func{bitwise_ptr : BitwiseBuiltin*}(values : felt*) -> (values : felt*): 533 | let (values) = keccak_round(values, 0x0000000000000001 * SHIFTS) 534 | let (values) = keccak_round(values, 0x0000000000008082 * SHIFTS) 535 | let (values) = keccak_round(values, 0x800000000000808A * SHIFTS) 536 | let (values) = keccak_round(values, 0x8000000080008000 * SHIFTS) 537 | let (values) = keccak_round(values, 0x000000000000808B * SHIFTS) 538 | let (values) = keccak_round(values, 0x0000000080000001 * SHIFTS) 539 | let (values) = keccak_round(values, 0x8000000080008081 * SHIFTS) 540 | let (values) = keccak_round(values, 0x8000000000008009 * SHIFTS) 541 | let (values) = keccak_round(values, 0x000000000000008A * SHIFTS) 542 | let (values) = keccak_round(values, 0x0000000000000088 * SHIFTS) 543 | let (values) = keccak_round(values, 0x0000000080008009 * SHIFTS) 544 | let (values) = keccak_round(values, 0x000000008000000A * SHIFTS) 545 | let (values) = keccak_round(values, 0x000000008000808B * SHIFTS) 546 | let (values) = keccak_round(values, 0x800000000000008B * SHIFTS) 547 | let (values) = keccak_round(values, 0x8000000000008089 * SHIFTS) 548 | let (values) = keccak_round(values, 0x8000000000008003 * SHIFTS) 549 | let (values) = keccak_round(values, 0x8000000000008002 * SHIFTS) 550 | let (values) = keccak_round(values, 0x8000000000000080 * SHIFTS) 551 | let (values) = keccak_round(values, 0x000000000000800A * SHIFTS) 552 | let (values) = keccak_round(values, 0x800000008000000A * SHIFTS) 553 | let (values) = keccak_round(values, 0x8000000080008081 * SHIFTS) 554 | let (values) = keccak_round(values, 0x8000000000008080 * SHIFTS) 555 | let (values) = keccak_round(values, 0x0000000080000001 * SHIFTS) 556 | let (values) = keccak_round(values, 0x8000000080008008 * SHIFTS) 557 | 558 | return (values) 559 | end 560 | 561 | -------------------------------------------------------------------------------- /programs/lib/secp/bigint.cairo: -------------------------------------------------------------------------------- 1 | # The base of the representation. 2 | const BASE = 2 ** 86 3 | 4 | # Represents an integer defined by 5 | # d0 + BASE * d1 + BASE**2 * d2. 6 | # Note that the limbs (d_i) are NOT restricted to the range [0, BASE) and in particular they 7 | # can be negative. 8 | # In most cases this is used to represent a secp256k1 field element. 9 | struct UnreducedBigInt3: 10 | member d0 : felt 11 | member d1 : felt 12 | member d2 : felt 13 | end 14 | 15 | # Same as UnreducedBigInt3, except that d0, d1 and d2 must be in the range [0, 3 * BASE). 16 | # In most cases this is used to represent a secp256k1 field element. 17 | struct BigInt3: 18 | member d0 : felt 19 | member d1 : felt 20 | member d2 : felt 21 | end 22 | 23 | # Represents a big integer: sum_i(BASE**i * d_i). 24 | # Note that the limbs (d_i) are NOT restricted to the range [0, BASE) and in particular they 25 | # can be negative. 26 | struct UnreducedBigInt5: 27 | member d0 : felt 28 | member d1 : felt 29 | member d2 : felt 30 | member d3 : felt 31 | member d4 : felt 32 | end 33 | 34 | func bigint_mul(x : BigInt3, y : BigInt3) -> (res : UnreducedBigInt5): 35 | return ( 36 | UnreducedBigInt5( 37 | d0=x.d0 * y.d0, 38 | d1=x.d0 * y.d1 + x.d1 * y.d0, 39 | d2=x.d0 * y.d2 + x.d1 * y.d1 + x.d2 * y.d0, 40 | d3=x.d1 * y.d2 + x.d2 * y.d1, 41 | d4=x.d2 * y.d2)) 42 | end 43 | 44 | # Returns a BigInt3 instance whose value is controlled by a prover hint. 45 | # 46 | # Soundness guarantee: each limb is in the range [0, 3 * BASE). 47 | # Completeness guarantee (honest prover): the value is in reduced form and in particular, 48 | # each limb is in the range [0, BASE). 49 | # 50 | # Hint arguments: value. 51 | func nondet_bigint3{range_check_ptr}() -> (res : BigInt3): 52 | # The result should be at the end of the stack after the function returns. 53 | let res : BigInt3 = [cast(ap + 5, BigInt3*)] 54 | %{ 55 | from starkware.cairo.common.cairo_secp.secp_utils import split 56 | segments.write_arg(ids.res.address_, split(value)) 57 | %} 58 | # The maximal possible sum of the limbs, assuming each of them is in the range [0, BASE). 59 | const MAX_SUM = 3 * (BASE - 1) 60 | assert [range_check_ptr] = MAX_SUM - (res.d0 + res.d1 + res.d2) 61 | 62 | # Prepare the result at the end of the stack. 63 | tempvar range_check_ptr = range_check_ptr + 4 64 | [range_check_ptr - 3] = res.d0; ap++ 65 | [range_check_ptr - 2] = res.d1; ap++ 66 | [range_check_ptr - 1] = res.d2; ap++ 67 | static_assert &res + BigInt3.SIZE == ap 68 | return (res=res) 69 | end 70 | -------------------------------------------------------------------------------- /programs/lib/secp/secp.cairo: -------------------------------------------------------------------------------- 1 | # Implementation of the ECDSA signature verification over the secp256k1 elliptic curve. 2 | # See information on the curve in secp_def.cairo. 3 | # 4 | # The generator point for the ECDSA is: 5 | # G = ( 6 | # 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 7 | # 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 8 | # ) 9 | 10 | from lib.secp.bigint import BASE, BigInt3, UnreducedBigInt3, bigint_mul, nondet_bigint3 11 | from lib.secp.secp_def import N0, N1, N2 12 | from lib.secp.secp_ec import EcPoint, ec_add, ec_mul 13 | from lib.secp.secp_field import unreduced_mul, unreduced_sqr, verify_zero 14 | 15 | from starkware.cairo.common.math import assert_nn_le, assert_not_zero, assert_in_range 16 | 17 | # Computes x * s^(-1) modulo the size of the elliptic curve (N). 18 | func mul_s_inv{range_check_ptr}(x : BigInt3, s : BigInt3) -> (res : BigInt3): 19 | %{ 20 | from starkware.cairo.common.cairo_secp.secp_utils import pack 21 | from starkware.python.math_utils import div_mod, safe_div 22 | 23 | N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 24 | x = pack(ids.x, PRIME) % N 25 | s = pack(ids.s, PRIME) % N 26 | value = res = div_mod(x, s, N) 27 | %} 28 | let (res) = nondet_bigint3() 29 | 30 | %{ value = k = safe_div(res * s - x, N) %} 31 | let (k) = nondet_bigint3() 32 | 33 | let (res_s) = bigint_mul(res, s) 34 | let n = BigInt3(N0, N1, N2) 35 | let (k_n) = bigint_mul(k, n) 36 | 37 | # We should now have res_s = k_n + x. Since the numbers are in unreduced form, 38 | # we should handle the carry. 39 | 40 | tempvar carry1 = (res_s.d0 - k_n.d0 - x.d0) / BASE 41 | assert [range_check_ptr + 0] = carry1 + 2 ** 127 42 | 43 | tempvar carry2 = (res_s.d1 - k_n.d1 - x.d1 + carry1) / BASE 44 | assert [range_check_ptr + 1] = carry2 + 2 ** 127 45 | 46 | tempvar carry3 = (res_s.d2 - k_n.d2 - x.d2 + carry2) / BASE 47 | assert [range_check_ptr + 2] = carry3 + 2 ** 127 48 | 49 | tempvar carry4 = (res_s.d3 - k_n.d3 + carry3) / BASE 50 | assert [range_check_ptr + 3] = carry4 + 2 ** 127 51 | 52 | assert res_s.d4 - k_n.d4 + carry4 = 0 53 | 54 | let range_check_ptr = range_check_ptr + 4 55 | 56 | return (res=res) 57 | end 58 | 59 | # Verifies that val is in the range [1, N). 60 | func validate_signature_entry{range_check_ptr}(val : BigInt3): 61 | assert_nn_le(val.d2, N2) 62 | assert_nn_le(val.d1, BASE - 1) 63 | assert_nn_le(val.d0, BASE - 1) 64 | 65 | if val.d2 == N2: 66 | if val.d1 == N1: 67 | assert_nn_le(val.d0, N0 - 1) 68 | return () 69 | end 70 | assert_nn_le(val.d1, N1 - 1) 71 | return () 72 | end 73 | 74 | if val.d2 == 0: 75 | if val.d1 == 0: 76 | # Make sure val > 0. 77 | assert_not_zero(val.d0) 78 | return () 79 | end 80 | end 81 | return () 82 | end 83 | 84 | # Verifies a Secp256k1 ECDSA signature. 85 | # Soundness assumptions: 86 | # * public_key_pt is on the curve. 87 | # * All the limbs of public_key_pt.x, public_key_pt.y, msg_hash are in the range [0, 3 * BASE). 88 | func verify_ecdsa{range_check_ptr}( 89 | public_key_pt : EcPoint, msg_hash : BigInt3, r : BigInt3, s : BigInt3): 90 | alloc_locals 91 | 92 | validate_signature_entry(r) 93 | validate_signature_entry(s) 94 | 95 | let gen_pt = EcPoint( 96 | BigInt3(0xe28d959f2815b16f81798, 0xa573a1c2c1c0a6ff36cb7, 0x79be667ef9dcbbac55a06), 97 | BigInt3(0x554199c47d08ffb10d4b8, 0x2ff0384422a3f45ed1229a, 0x483ada7726a3c4655da4f)) 98 | 99 | # Compute u1 and u2. 100 | let (u1 : BigInt3) = mul_s_inv(msg_hash, s) 101 | let (u2 : BigInt3) = mul_s_inv(r, s) 102 | 103 | let (gen_u1) = ec_mul(gen_pt, u1) 104 | let (pub_u2) = ec_mul(public_key_pt, u2) 105 | let (res) = ec_add(gen_u1, pub_u2) 106 | 107 | # The following assert also implies that res is not the zero point. 108 | assert res.x = r 109 | return () 110 | end 111 | 112 | func ecdsa_raw_recover{range_check_ptr}( 113 | msg_hash : BigInt3, R_x : BigInt3, R_y : BigInt3, s : BigInt3, v : felt) -> (res : EcPoint): 114 | alloc_locals 115 | 116 | let gen_pt = EcPoint( 117 | BigInt3(0xe28d959f2815b16f81798, 0xa573a1c2c1c0a6ff36cb7, 0x79be667ef9dcbbac55a06), 118 | BigInt3(0x554199c47d08ffb10d4b8, 0x2ff0384422a3f45ed1229a, 0x483ada7726a3c4655da4f)) 119 | 120 | # Compute y-coordinate of R and (TODO) check soundness 121 | #%{ 122 | # from starkware.cairo.common.cairo_secp.secp_utils import pack 123 | # from starkware.python.math_utils import sqrt 124 | # P = 2**256 - 4294968273 125 | # r = pack(ids.r, PRIME) 126 | # R_y = sqrt(r**3 + 7, P) 127 | # value = R_y = R_y if ids.v == 27 else -R_y 128 | #%} 129 | #let (R_y : BigInt3) = nondet_bigint3() 130 | 131 | # Compute r^{-1} 132 | let (r_inv) = mul_s_inv(BigInt3(d0=1, d1=0, d2=0), R_x) 133 | 134 | let R = EcPoint(R_x, R_y) 135 | let (x_1) = ec_mul(R, s) 136 | let (x_2) = ec_mul(gen_pt, msg_hash) 137 | let (x_2_neg) = ec_mul(x_2, BigInt3(N0-1, N1, N2)) # Compute -x_2 138 | let (x) = ec_add(x_1, x_2_neg) 139 | let (Q) = ec_mul(x, r_inv) 140 | 141 | return (res=Q) 142 | end 143 | 144 | func recover_public_key_from_msg_hash{range_check_ptr}( 145 | msg_hash : BigInt3, v : felt, r : BigInt3, s : BigInt3) -> (res : EcPoint): 146 | alloc_locals 147 | 148 | validate_signature_entry(r) 149 | validate_signature_entry(s) 150 | assert_in_range(v, 27, 28) 151 | 152 | let (res) = ecdsa_raw_recover(msg_hash, v, r, s) 153 | return (res=res) 154 | end 155 | -------------------------------------------------------------------------------- /programs/lib/secp/secp_def.cairo: -------------------------------------------------------------------------------- 1 | # Basic definitions for the secp256k1 elliptic curve. 2 | # The curve is given by the equation 3 | # y^2 = x^3 + 7 4 | # over the field Z/p for 5 | # p = secp256k1_prime = 2 ** 256 - (2 ** 32 + 2 ** 9 + 2 ** 8 + 2 ** 7 + 2 ** 6 + 2 ** 4 + 1). 6 | # The size of the curve is 7 | # n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 (prime). 8 | 9 | # SECP_REM is defined by the equation: 10 | # secp256k1_prime = 2 ** 256 - SECP_REM. 11 | const SECP_REM = 2 ** 32 + 2 ** 9 + 2 ** 8 + 2 ** 7 + 2 ** 6 + 2 ** 4 + 1 12 | 13 | # The following constants represent the size of the secp256k1 curve: 14 | # n = N0 + BASE * N1 + BASE**2 * N2. 15 | const N0 = 0x8a03bbfd25e8cd0364141 16 | const N1 = 0x3ffffffffffaeabb739abd 17 | const N2 = 0xfffffffffffffffffffff 18 | -------------------------------------------------------------------------------- /programs/lib/secp/secp_ec.cairo: -------------------------------------------------------------------------------- 1 | from lib.secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3 2 | from lib.secp.secp_field import is_zero, unreduced_mul, unreduced_sqr, verify_zero 3 | 4 | # Represents a point on the elliptic curve. 5 | # The zero point is represented using pt.x=0, as there is no point on the curve with this x value. 6 | struct EcPoint: 7 | member x : BigInt3 8 | member y : BigInt3 9 | end 10 | 11 | # Returns the slope of the elliptic curve at the given point. 12 | # The slope is used to compute pt + pt. 13 | # Assumption: pt != 0. 14 | func compute_doubling_slope{range_check_ptr}(pt : EcPoint) -> (slope : BigInt3): 15 | # Note that y cannot be zero: assume that it is, then pt = -pt, so 2 * pt = 0, which 16 | # contradicts the fact that the size of the curve is odd. 17 | %{ 18 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack 19 | from starkware.python.math_utils import div_mod 20 | 21 | # Compute the slope. 22 | x = pack(ids.pt.x, PRIME) 23 | y = pack(ids.pt.y, PRIME) 24 | value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P) 25 | %} 26 | let (slope : BigInt3) = nondet_bigint3() 27 | 28 | let (x_sqr : UnreducedBigInt3) = unreduced_sqr(pt.x) 29 | let (slope_y : UnreducedBigInt3) = unreduced_mul(slope, pt.y) 30 | 31 | verify_zero( 32 | UnreducedBigInt3( 33 | d0=3 * x_sqr.d0 - 2 * slope_y.d0, 34 | d1=3 * x_sqr.d1 - 2 * slope_y.d1, 35 | d2=3 * x_sqr.d2 - 2 * slope_y.d2)) 36 | 37 | return (slope=slope) 38 | end 39 | 40 | # Returns the slope of the line connecting the two given points. 41 | # The slope is used to compute pt0 + pt1. 42 | # Assumption: pt0.x != pt1.x (mod secp256k1_prime). 43 | func compute_slope{range_check_ptr}(pt0 : EcPoint, pt1 : EcPoint) -> (slope : BigInt3): 44 | %{ 45 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack 46 | from starkware.python.math_utils import div_mod 47 | 48 | # Compute the slope. 49 | x0 = pack(ids.pt0.x, PRIME) 50 | y0 = pack(ids.pt0.y, PRIME) 51 | x1 = pack(ids.pt1.x, PRIME) 52 | y1 = pack(ids.pt1.y, PRIME) 53 | value = slope = div_mod(y0 - y1, x0 - x1, SECP_P) 54 | %} 55 | let (slope) = nondet_bigint3() 56 | 57 | let x_diff = BigInt3(d0=pt0.x.d0 - pt1.x.d0, d1=pt0.x.d1 - pt1.x.d1, d2=pt0.x.d2 - pt1.x.d2) 58 | let (x_diff_slope : UnreducedBigInt3) = unreduced_mul(x_diff, slope) 59 | 60 | verify_zero( 61 | UnreducedBigInt3( 62 | d0=x_diff_slope.d0 - pt0.y.d0 + pt1.y.d0, 63 | d1=x_diff_slope.d1 - pt0.y.d1 + pt1.y.d1, 64 | d2=x_diff_slope.d2 - pt0.y.d2 + pt1.y.d2)) 65 | 66 | return (slope) 67 | end 68 | 69 | # Given a point 'pt' on the elliptic curve, computes pt + pt. 70 | func ec_double{range_check_ptr}(pt : EcPoint) -> (res : EcPoint): 71 | if pt.x.d0 == 0: 72 | if pt.x.d1 == 0: 73 | if pt.x.d2 == 0: 74 | return (pt) 75 | end 76 | end 77 | end 78 | 79 | let (slope : BigInt3) = compute_doubling_slope(pt) 80 | let (slope_sqr : UnreducedBigInt3) = unreduced_sqr(slope) 81 | 82 | %{ 83 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack 84 | 85 | slope = pack(ids.slope, PRIME) 86 | x = pack(ids.pt.x, PRIME) 87 | y = pack(ids.pt.y, PRIME) 88 | 89 | value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P 90 | %} 91 | let (new_x : BigInt3) = nondet_bigint3() 92 | 93 | %{ value = new_y = (slope * (x - new_x) - y) % SECP_P %} 94 | let (new_y : BigInt3) = nondet_bigint3() 95 | 96 | verify_zero( 97 | UnreducedBigInt3( 98 | d0=slope_sqr.d0 - new_x.d0 - 2 * pt.x.d0, 99 | d1=slope_sqr.d1 - new_x.d1 - 2 * pt.x.d1, 100 | d2=slope_sqr.d2 - new_x.d2 - 2 * pt.x.d2)) 101 | 102 | let (x_diff_slope : UnreducedBigInt3) = unreduced_mul( 103 | BigInt3(d0=pt.x.d0 - new_x.d0, d1=pt.x.d1 - new_x.d1, d2=pt.x.d2 - new_x.d2), slope) 104 | 105 | verify_zero( 106 | UnreducedBigInt3( 107 | d0=x_diff_slope.d0 - pt.y.d0 - new_y.d0, 108 | d1=x_diff_slope.d1 - pt.y.d1 - new_y.d1, 109 | d2=x_diff_slope.d2 - pt.y.d2 - new_y.d2)) 110 | 111 | return (EcPoint(new_x, new_y)) 112 | end 113 | 114 | # Adds two points on the elliptic curve. 115 | # Assumption: pt0.x != pt1.x (however, pt0 = pt1 = 0 is allowed). 116 | # Note that this means that the function cannot be used if pt0 = pt1 117 | # (use ec_double() in this case) or pt0 = -pt1 (the result is 0 in this case). 118 | func fast_ec_add{range_check_ptr}(pt0 : EcPoint, pt1 : EcPoint) -> (res : EcPoint): 119 | if pt0.x.d0 == 0: 120 | if pt0.x.d1 == 0: 121 | if pt0.x.d2 == 0: 122 | return (pt1) 123 | end 124 | end 125 | end 126 | if pt1.x.d0 == 0: 127 | if pt1.x.d1 == 0: 128 | if pt1.x.d2 == 0: 129 | return (pt0) 130 | end 131 | end 132 | end 133 | 134 | let (slope : BigInt3) = compute_slope(pt0, pt1) 135 | let (slope_sqr : UnreducedBigInt3) = unreduced_sqr(slope) 136 | 137 | %{ 138 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack 139 | 140 | slope = pack(ids.slope, PRIME) 141 | x0 = pack(ids.pt0.x, PRIME) 142 | x1 = pack(ids.pt1.x, PRIME) 143 | y0 = pack(ids.pt0.y, PRIME) 144 | 145 | value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P 146 | %} 147 | let (new_x : BigInt3) = nondet_bigint3() 148 | 149 | %{ value = new_y = (slope * (x0 - new_x) - y0) % SECP_P %} 150 | let (new_y : BigInt3) = nondet_bigint3() 151 | 152 | verify_zero( 153 | UnreducedBigInt3( 154 | d0=slope_sqr.d0 - new_x.d0 - pt0.x.d0 - pt1.x.d0, 155 | d1=slope_sqr.d1 - new_x.d1 - pt0.x.d1 - pt1.x.d1, 156 | d2=slope_sqr.d2 - new_x.d2 - pt0.x.d2 - pt1.x.d2)) 157 | 158 | let (x_diff_slope : UnreducedBigInt3) = unreduced_mul( 159 | BigInt3(d0=pt0.x.d0 - new_x.d0, d1=pt0.x.d1 - new_x.d1, d2=pt0.x.d2 - new_x.d2), slope) 160 | 161 | verify_zero( 162 | UnreducedBigInt3( 163 | d0=x_diff_slope.d0 - pt0.y.d0 - new_y.d0, 164 | d1=x_diff_slope.d1 - pt0.y.d1 - new_y.d1, 165 | d2=x_diff_slope.d2 - pt0.y.d2 - new_y.d2)) 166 | 167 | return (EcPoint(new_x, new_y)) 168 | end 169 | 170 | # Same as fast_ec_add, except that the cases pt0 = ±pt1 are supported. 171 | func ec_add{range_check_ptr}(pt0 : EcPoint, pt1 : EcPoint) -> (res : EcPoint): 172 | let x_diff = BigInt3(d0=pt0.x.d0 - pt1.x.d0, d1=pt0.x.d1 - pt1.x.d1, d2=pt0.x.d2 - pt1.x.d2) 173 | let (same_x : felt) = is_zero(x_diff) 174 | if same_x == 0: 175 | # pt0.x != pt1.x so we can use fast_ec_add. 176 | return fast_ec_add(pt0, pt1) 177 | end 178 | 179 | # We have pt0.x = pt1.x. This implies pt0.y = ±pt1.y. 180 | # Check whether pt0.y = -pt1.y. 181 | let y_sum = BigInt3(d0=pt0.y.d0 + pt1.y.d0, d1=pt0.y.d1 + pt1.y.d1, d2=pt0.y.d2 + pt1.y.d2) 182 | let (opposite_y : felt) = is_zero(y_sum) 183 | if opposite_y != 0: 184 | # pt0.y = -pt1.y. 185 | # Note that the case pt0 = pt1 = 0 falls into this branch as well. 186 | let ZERO_POINT = EcPoint(BigInt3(0, 0, 0), BigInt3(0, 0, 0)) 187 | return (ZERO_POINT) 188 | else: 189 | # pt0.y = pt1.y. 190 | return ec_double(pt0) 191 | end 192 | end 193 | 194 | # Given 0 <= m < 250, a scalar and a point on the elliptic curve, pt, 195 | # verifies that 0 <= scalar < 2**m and returns (2**m * pt, scalar * pt). 196 | func ec_mul_inner{range_check_ptr}(pt : EcPoint, scalar : felt, m : felt) -> ( 197 | pow2 : EcPoint, res : EcPoint): 198 | if m == 0: 199 | assert scalar = 0 200 | let ZERO_POINT = EcPoint(BigInt3(0, 0, 0), BigInt3(0, 0, 0)) 201 | return (pow2=pt, res=ZERO_POINT) 202 | end 203 | 204 | alloc_locals 205 | let (double_pt : EcPoint) = ec_double(pt) 206 | %{ memory[ap] = (ids.scalar % PRIME) % 2 %} 207 | jmp odd if [ap] != 0; ap++ 208 | return ec_mul_inner(pt=double_pt, scalar=scalar / 2, m=m - 1) 209 | 210 | odd: 211 | let (local inner_pow2 : EcPoint, inner_res : EcPoint) = ec_mul_inner( 212 | pt=double_pt, scalar=(scalar - 1) / 2, m=m - 1) 213 | # Here inner_res = (scalar - 1) / 2 * double_pt = (scalar - 1) * pt. 214 | # Assume pt != 0 and that inner_res = ±pt. We obtain (scalar - 1) * pt = ±pt => 215 | # scalar - 1 = ±1 (mod N) => scalar = 0 or 2. 216 | # In both cases (scalar - 1) / 2 cannot be in the range [0, 2**(m-1)), so we get a 217 | # contradiction. 218 | let (res : EcPoint) = fast_ec_add(pt0=pt, pt1=inner_res) 219 | return (pow2=inner_pow2, res=res) 220 | end 221 | 222 | func ec_mul{range_check_ptr}(pt : EcPoint, scalar : BigInt3) -> (res : EcPoint): 223 | alloc_locals 224 | let (pow2_0 : EcPoint, local res0 : EcPoint) = ec_mul_inner(pt, scalar.d0, 86) 225 | let (pow2_1 : EcPoint, local res1 : EcPoint) = ec_mul_inner(pow2_0, scalar.d1, 86) 226 | let (_, local res2 : EcPoint) = ec_mul_inner(pow2_1, scalar.d2, 84) 227 | let (res : EcPoint) = ec_add(res0, res1) 228 | let (res : EcPoint) = ec_add(res, res2) 229 | return (res) 230 | end 231 | -------------------------------------------------------------------------------- /programs/lib/secp/secp_field.cairo: -------------------------------------------------------------------------------- 1 | from lib.secp.bigint import BASE, BigInt3, UnreducedBigInt3, nondet_bigint3 2 | from lib.secp.secp_def import SECP_REM 3 | 4 | # Multiplies two values modulo the secp256k1 prime. 5 | # The returned limbs may be above 3 * BASE. 6 | # 7 | # If each of the input limbs is in the range (-x, x), the result's limbs are guaranteed to be 8 | # in the range (-x**2 * (2 ** 35.01), x**2 * (2 ** 35.01)). 9 | # 10 | # This means that if unreduced_mul is called on the result of nondet_bigint3, or the difference 11 | # between two such results, we have: 12 | # Soundness guarantee: the limbs are in the range (-2**208.6, 2**208.6). 13 | # Completeness guarantee: the limbs are in the range (-2**207.01, 2**207.01). 14 | func unreduced_mul(a : BigInt3, b : BigInt3) -> (res_low : UnreducedBigInt3): 15 | # The result of the product is: 16 | # sum_{i, j} a.d_i * b.d_j * BASE**(i + j) 17 | # Since we are computing it mod secp256k1_prime, we replace the term 18 | # a.d_i * b.d_j * BASE**(i + j) 19 | # where i + j >= 3 with 20 | # a.d_i * b.d_j * BASE**(i + j - 3) * 4 * SECP_REM 21 | # since BASE ** 3 = 4 * SECP_REM (mod secp256k1_prime). 22 | return ( 23 | UnreducedBigInt3( 24 | d0=a.d0 * b.d0 + (a.d1 * b.d2 + a.d2 * b.d1) * (4 * SECP_REM), 25 | d1=a.d0 * b.d1 + a.d1 * b.d0 + (a.d2 * b.d2) * (4 * SECP_REM), 26 | d2=a.d0 * b.d2 + a.d1 * b.d1 + a.d2 * b.d0)) 27 | end 28 | 29 | # Computes the square of the given value modulo the secp256k1 prime. 30 | # It has the same guarantees as in unreduced_mul(a, a). 31 | func unreduced_sqr(a : BigInt3) -> (res_low : UnreducedBigInt3): 32 | tempvar twice_d0 = a.d0 * 2 33 | return ( 34 | UnreducedBigInt3( 35 | d0=a.d0 * a.d0 + (a.d1 * a.d2) * (2 * 4 * SECP_REM), 36 | d1=twice_d0 * a.d1 + (a.d2 * a.d2) * (4 * SECP_REM), 37 | d2=twice_d0 * a.d2 + a.d1 * a.d1)) 38 | end 39 | 40 | # Verifies that the given unreduced value is equal to zero modulo the secp256k1 prime. 41 | # Completeness assumption: val's limbs are in the range (-2**210.99, 2**210.99). 42 | # Soundness assumption: val's limbs are in the range (-2**250, 2**250). 43 | func verify_zero{range_check_ptr}(val : UnreducedBigInt3): 44 | let q = [ap] 45 | %{ 46 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P 47 | q, r = divmod(pack(ids.val, PRIME), SECP_P) 48 | assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." 49 | ids.q = q % PRIME 50 | %} 51 | let q_biased = [ap + 1] 52 | q_biased = q + 2 ** 127; ap++ 53 | [range_check_ptr] = q_biased; ap++ 54 | 55 | tempvar r1 = (val.d0 + q * SECP_REM) / BASE 56 | assert [range_check_ptr + 1] = r1 + 2 ** 127 57 | # This implies r1 * BASE = val.d0 + q * SECP_REM (as integers). 58 | 59 | tempvar r2 = (val.d1 + r1) / BASE 60 | assert [range_check_ptr + 2] = r2 + 2 ** 127 61 | # This implies r2 * BASE = val.d1 + r1 (as integers). 62 | # Therefore, r2 * BASE**2 = val.d1 * BASE + r1 * BASE. 63 | 64 | assert val.d2 = q * (BASE / 4) - r2 65 | # This implies q * BASE / 4 = val.d2 + r2 (as integers). 66 | # Therefore, 67 | # q * BASE**3 / 4 = val.d2 * BASE**2 + r2 * BASE ** 2 = 68 | # val.d2 * BASE**2 + val.d1 * BASE + r1 * BASE = 69 | # val.d2 * BASE**2 + val.d1 * BASE + val.d0 + q * SECP_REM = 70 | # val + q * SECP_REM. 71 | # Hence, val = q * (BASE**3 / 4 - SECP_REM) = q * (2**256 - SECP_REM). 72 | 73 | let range_check_ptr = range_check_ptr + 3 74 | return () 75 | end 76 | 77 | # Returns 1 if x == 0 (mod secp256k1_prime), and 0 otherwise. 78 | func is_zero{range_check_ptr}(x : BigInt3) -> (res : felt): 79 | %{ 80 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack 81 | x = pack(ids.x, PRIME) % SECP_P 82 | %} 83 | %{ memory[ap] = int(x == 0) %} 84 | tempvar x_is_zero 85 | if x_is_zero != 0: 86 | verify_zero(UnreducedBigInt3(d0=x.d0, d1=x.d1, d2=x.d2)) 87 | return (res=1) 88 | end 89 | 90 | %{ 91 | from starkware.cairo.common.cairo_secp.secp_utils import SECP_P 92 | from starkware.python.math_utils import div_mod 93 | 94 | value = x_inv = div_mod(1, x, SECP_P) 95 | %} 96 | let (x_inv) = nondet_bigint3() 97 | let (x_x_inv) = unreduced_mul(x, x_inv) 98 | 99 | # Check that x * x_inv = 1 to verify that x != 0. 100 | verify_zero(UnreducedBigInt3( 101 | d0=x_x_inv.d0 - 1, 102 | d1=x_x_inv.d1, 103 | d2=x_x_inv.d2)) 104 | return (res=0) 105 | end 106 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/address.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.math import assert_le, unsigned_div_rem 2 | from starkware.cairo.common.math_cmp import is_le 3 | 4 | from lib.storage_verification.types import Address 5 | from lib.storage_verification.pow import pow 6 | from lib.storage_verification.bitshift import bitshift_right, bitshift_left 7 | 8 | func address_words64_to_160bit{ range_check_ptr }(input: Address) -> (res: felt): 9 | alloc_locals 10 | let (local word_1_exponent) = pow(2, 8 * 12) 11 | let (local word_2_exponent) = pow(2, 8 * 4) 12 | 13 | let result = (input.word_1 * word_1_exponent) + (input.word_2 * word_2_exponent) + input.word_3 14 | return (result) 15 | end 16 | 17 | func address_160bit_to_words64{ range_check_ptr }(input: felt) -> (res: Address): 18 | alloc_locals 19 | 20 | let (local eigth_byte_exponent) = pow(2, 8 * 8) 21 | let (local four_byte_exponent) = pow(2, 4 * 8) 22 | 23 | let (tmp, third_word) = unsigned_div_rem(input, four_byte_exponent) 24 | 25 | let third_word_max_size = 2**32 - 1 26 | assert_le(third_word, third_word_max_size) 27 | 28 | let (first_word, second_word) = unsigned_div_rem(tmp, eigth_byte_exponent) 29 | local res: Address = Address(first_word, second_word, third_word) 30 | return (res) 31 | end 32 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/bitset.cairo: -------------------------------------------------------------------------------- 1 | from lib.storage_verification.pow import pow 2 | from starkware.cairo.common.math import unsigned_div_rem 3 | 4 | # Argument bitset: 4bit number in range(0, 15) 5 | # Argument position: counted from the left 6 | # Returns a number in range(0, 1) 7 | func bitset4_get{ range_check_ptr }(bitset: felt, position: felt) -> (res: felt): 8 | let (divider) = pow(2, 4 - position) 9 | let (q1, r1) = unsigned_div_rem(bitset, divider) 10 | let (q, r) = unsigned_div_rem(q1, 2) 11 | return (r) 12 | end 13 | 14 | # Argument bitset: 6bit number in range(0, 63) 15 | # Argument position: counted from the left 16 | # Returns a number in range(0, 1) 17 | func bitset6_get{ range_check_ptr }(bitset: felt, position: felt) -> (res: felt): 18 | let (divider) = pow(2, 6 - position) 19 | let (q1, r1) = unsigned_div_rem(bitset, divider) 20 | let (q, r) = unsigned_div_rem(q1, 2) 21 | return (r) 22 | end 23 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/bitshift.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.math import assert_le 2 | from starkware.cairo.common.math import unsigned_div_rem 3 | from lib.storage_verification.pow import pow 4 | 5 | 6 | func bitshift_right{ range_check_ptr }(word: felt, num_bits: felt) -> (shifted: felt): 7 | # verifies word fits in 64bits 8 | assert_le(word, 2**64 - 1) 9 | 10 | # verifies shifted bits are not above 64 11 | assert_le(num_bits, 64) 12 | 13 | let (divider) = pow(2, num_bits) 14 | let (left_part, _) = unsigned_div_rem(word, divider) 15 | return (left_part) 16 | end 17 | 18 | func bitshift_left{ range_check_ptr }(word: felt, num_bits: felt) -> (shifted: felt): 19 | # verifies word fits in 64bits 20 | assert_le(word, 2**64 - 1) 21 | 22 | # verifies shifted bits are not above 64 23 | assert_le(num_bits, 64) 24 | 25 | let (multiplicator) = pow(2, num_bits) 26 | 27 | let k = word * multiplicator 28 | let (q, r) = unsigned_div_rem(k, 2**64) 29 | 30 | return (r) 31 | end 32 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/blockheader_rlp_extractor.cairo: -------------------------------------------------------------------------------- 1 | from lib.storage_verification.extract_from_rlp import extract_data, extractElement, jumpOverElement 2 | from lib.storage_verification.types import (Keccak256Hash, Address, IntsSequence) 3 | 4 | ### Elements decoder 5 | 6 | func decode_parent_hash{ range_check_ptr }(block_rlp: IntsSequence) -> (res: Keccak256Hash): 7 | alloc_locals 8 | let (local data: IntsSequence) = extract_data(4, 32, block_rlp) 9 | let parent_hash = data.element 10 | local hash: Keccak256Hash = Keccak256Hash( 11 | word_1=parent_hash[0], 12 | word_2=parent_hash[1], 13 | word_3=parent_hash[2], 14 | word_4=parent_hash[3] 15 | ) 16 | return (hash) 17 | end 18 | 19 | func decode_uncles_hash{ range_check_ptr }(block_rlp: IntsSequence) -> (res: Keccak256Hash): 20 | alloc_locals 21 | let (local data: IntsSequence) = extract_data(4+32+1, 32, block_rlp) 22 | let uncles_hash = data.element 23 | local hash: Keccak256Hash = Keccak256Hash( 24 | word_1=uncles_hash[0], 25 | word_2=uncles_hash[1], 26 | word_3=uncles_hash[2], 27 | word_4=uncles_hash[3] 28 | ) 29 | return (hash) 30 | end 31 | 32 | func decode_beneficiary{ range_check_ptr }(block_rlp: IntsSequence) -> (res: Address): 33 | alloc_locals 34 | let (local data: IntsSequence) = extract_data(4+32+1+32+1, 20, block_rlp) 35 | let beneficiary = data.element 36 | local address: Address = Address( 37 | word_1=beneficiary[0], 38 | word_2=beneficiary[1], 39 | word_3=beneficiary[2] 40 | ) 41 | return (address) 42 | end 43 | 44 | func decode_state_root{ range_check_ptr }(block_rlp: IntsSequence) -> (res: Keccak256Hash): 45 | alloc_locals 46 | let (local data: IntsSequence) = extract_data(4+32+1+32+1+20+1, 32, block_rlp) 47 | let state_root = data.element 48 | local hash: Keccak256Hash = Keccak256Hash( 49 | word_1=state_root[0], 50 | word_2=state_root[1], 51 | word_3=state_root[2], 52 | word_4=state_root[3] 53 | ) 54 | return (hash) 55 | end 56 | 57 | func decode_transactions_root{ range_check_ptr }(block_rlp: IntsSequence) -> (res: Keccak256Hash): 58 | alloc_locals 59 | let (local data: IntsSequence) = extract_data(4+32+1+32+1+20+1+32+1, 32, block_rlp) 60 | let transactions_root = data.element 61 | local hash: Keccak256Hash = Keccak256Hash( 62 | word_1=transactions_root[0], 63 | word_2=transactions_root[1], 64 | word_3=transactions_root[2], 65 | word_4=transactions_root[3] 66 | ) 67 | return (hash) 68 | end 69 | 70 | func decode_receipts_root{ range_check_ptr }(block_rlp: IntsSequence) -> (res: Keccak256Hash): 71 | alloc_locals 72 | let (local data: IntsSequence) = extract_data(4+32+1+32+1+20+1+32+1+32+1, 32, block_rlp) 73 | let receipts_root = data.element 74 | local hash: Keccak256Hash = Keccak256Hash( 75 | word_1=receipts_root[0], 76 | word_2=receipts_root[1], 77 | word_3=receipts_root[2], 78 | word_4=receipts_root[3] 79 | ) 80 | return (hash) 81 | end 82 | 83 | func decode_difficulty{ range_check_ptr }(block_rlp: IntsSequence) -> (res: felt): 84 | alloc_locals 85 | let (local difficulty_rlp_element: IntsSequence) = extractElement(block_rlp, 448) 86 | local difficulty = difficulty_rlp_element.element[0] 87 | return (difficulty) 88 | end 89 | 90 | func decode_block_number{ range_check_ptr }(block_rlp: IntsSequence) -> (res: felt): 91 | alloc_locals 92 | let (blockNumberPosition) = jumpOverElement(block_rlp, 448) 93 | let (local block_number_rlp_element: IntsSequence) = extractElement(block_rlp, blockNumberPosition) 94 | local block_number = block_number_rlp_element.element[0] 95 | return (block_number) 96 | end 97 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/comp_arr.cairo: -------------------------------------------------------------------------------- 1 | func arr_eq(a: felt*, a_len: felt, b: felt*, b_len: felt) -> (res: felt): 2 | if a_len != b_len: 3 | return (0) 4 | end 5 | if a_len == 0: 6 | return (1) 7 | end 8 | return _arr_eq(a=a, a_len=a_len, b=b, b_len=b_len, current_index=a_len-1) 9 | end 10 | 11 | func _arr_eq(a: felt*, a_len: felt, b: felt*, b_len: felt, current_index: felt) -> (res: felt): 12 | if current_index == -1: 13 | return (1) 14 | end 15 | 16 | if a[current_index] != b[current_index]: 17 | return (0) 18 | end 19 | return _arr_eq(a=a, a_len=a_len, b=b, b_len=b_len, current_index=current_index-1) 20 | end -------------------------------------------------------------------------------- /programs/lib/storage_verification/concat_arr.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.memcpy import memcpy 2 | from starkware.cairo.common.alloc import alloc 3 | 4 | func concat_arr{range_check_ptr}( 5 | acc: felt*, 6 | acc_len: felt, 7 | arr: felt*, 8 | arr_len: felt 9 | ) -> (res: felt*, res_len: felt): 10 | alloc_locals 11 | let (local acc_cpy: felt*) = alloc() 12 | 13 | memcpy(acc_cpy, acc, acc_len) 14 | 15 | memcpy(acc_cpy + acc_len, arr, arr_len) 16 | return (acc_cpy, acc_len + arr_len) 17 | end -------------------------------------------------------------------------------- /programs/lib/storage_verification/extract_from_rlp.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 2 | from starkware.cairo.common.math import unsigned_div_rem 3 | from starkware.cairo.common.math_cmp import is_le 4 | from starkware.cairo.common.alloc import alloc 5 | 6 | from lib.storage_verification.types import IntsSequence, RLPItem 7 | from lib.storage_verification.bitshift import bitshift_right, bitshift_left 8 | from lib.storage_verification.pow import pow 9 | 10 | 11 | func getElement{ range_check_ptr }(rlp: IntsSequence, position: felt) -> (res: RLPItem): 12 | alloc_locals 13 | 14 | let (local data: IntsSequence) = extract_data{ range_check_ptr = range_check_ptr }(position, 1, rlp) 15 | let firstByteArr: felt* = data.element 16 | let firstByte = firstByteArr[0] 17 | 18 | let (le_127) = is_le(firstByte, 127) 19 | 20 | if le_127 == 1: 21 | local result: RLPItem = RLPItem(firstByte, position, 1) 22 | return (result) 23 | end 24 | 25 | let (le_183) = is_le(firstByte, 183) 26 | if le_183 == 1: 27 | local result: RLPItem = RLPItem(firstByte, position+1, firstByte-128) 28 | return (result) 29 | end 30 | 31 | let (le_191) = is_le(firstByte, 191) 32 | if le_191 == 1: 33 | let lengthOfLength = firstByte - 183 34 | let (local bytes: IntsSequence) = extract_data{ range_check_ptr = range_check_ptr }(position + 1, lengthOfLength, rlp) 35 | let lengthArr: felt* = bytes.element 36 | let length = lengthArr[0] 37 | 38 | local result: RLPItem = RLPItem(firstByte, position + 1 + lengthOfLength, length) 39 | return (result) 40 | end 41 | 42 | let (le_247) = is_le(firstByte, 247) 43 | if le_247 == 1: 44 | local result: RLPItem = RLPItem(firstByte, position+1, firstByte-192) 45 | return (result) 46 | end 47 | 48 | let lengthOfLength = firstByte - 247 49 | let (local bytes: IntsSequence) = extract_data{ range_check_ptr = range_check_ptr }(position+1, lengthOfLength, rlp) 50 | let lengthArr: felt* = bytes.element 51 | let length = lengthArr[0] 52 | 53 | local result: RLPItem = RLPItem(firstByte, position + 1 + lengthOfLength, length) 54 | return (result) 55 | end 56 | 57 | func extractElement{ range_check_ptr }(rlp: IntsSequence, position: felt) -> (res: IntsSequence): 58 | alloc_locals 59 | let (rlpItem: RLPItem) = getElement{ range_check_ptr = range_check_ptr }(rlp, position) 60 | 61 | if rlpItem.length == 0: 62 | let (local element: felt*) = alloc() 63 | local result: IntsSequence = IntsSequence(element, 0, 0) 64 | tempvar range_check_ptr = range_check_ptr 65 | return (result) 66 | else: 67 | tempvar range_check_ptr = range_check_ptr 68 | end 69 | 70 | return extract_data{ range_check_ptr = range_check_ptr }(rlpItem.dataPosition, rlpItem.length, rlp) 71 | end 72 | 73 | func jumpOverElement{ range_check_ptr }(rlp: IntsSequence, position: felt) -> (res: felt): 74 | let (rlpItem: RLPItem) = getElement{ range_check_ptr = range_check_ptr }(rlp, position) 75 | return (rlpItem.dataPosition + rlpItem.length) 76 | end 77 | 78 | func extract_data{ range_check_ptr }(start_pos: felt, size: felt, rlp: IntsSequence) -> (res: IntsSequence): 79 | alloc_locals 80 | let (start_word, left_shift) = unsigned_div_rem(start_pos, 8) 81 | let (end_word_tmp, end_pos_tmp) = unsigned_div_rem(start_pos + size, 8) 82 | 83 | let (full_words, remainder) = unsigned_div_rem(size, 8) 84 | 85 | # end_pos is a bad name - it conflicts with start_pos 86 | # start_pos is absolute, and end_pos is relative inside the world 87 | local end_pos 88 | local end_word 89 | if end_pos_tmp == 0: 90 | end_pos = 8 91 | end_word = end_word_tmp - 1 92 | else: 93 | end_pos = end_pos_tmp 94 | end_word = end_word_tmp 95 | end 96 | 97 | let (_, last_rlp_word_len_tmp) = unsigned_div_rem(rlp.element_size_bytes, 8) 98 | local last_rlp_word_len 99 | if last_rlp_word_len_tmp == 0: 100 | last_rlp_word_len = 8 101 | else: 102 | last_rlp_word_len = last_rlp_word_len_tmp 103 | end 104 | 105 | local right_shift = 8 - left_shift 106 | let last_word_right_shift = last_rlp_word_len - left_shift 107 | 108 | let (local new_words : felt*) = alloc() 109 | 110 | let (local new_words_len) = extract_data_rec( 111 | start_word=start_word, 112 | full_words=full_words, 113 | left_shift=left_shift, 114 | right_shift=right_shift, 115 | last_word_right_shift=last_word_right_shift, 116 | rlp=rlp, 117 | acc=new_words, 118 | acc_len=0, 119 | current_index=start_word) 120 | 121 | local result_words_len 122 | 123 | if remainder == 0: 124 | result_words_len = new_words_len 125 | tempvar range_check_ptr = range_check_ptr 126 | else: 127 | let (local left_shift_above_8_bytes) = is_le(8 + 1, remainder + left_shift) 128 | if left_shift_above_8_bytes == 1: 129 | let (local left_part) = bitshift_left(rlp.element[end_word - 1], left_shift * 8) 130 | 131 | local right_part 132 | if end_word == rlp.element_size_words - 1: 133 | let (local is_last_word_right_shift_negative) = is_le(last_word_right_shift + 8, 7) 134 | if is_last_word_right_shift_negative == 1: 135 | let (local right_part_tmp) = bitshift_left(rlp.element[end_word], -8 * last_word_right_shift) 136 | right_part = right_part_tmp 137 | tempvar range_check_ptr = range_check_ptr 138 | else: 139 | let (local right_part_tmp) = bitshift_right(rlp.element[end_word], 8 * last_word_right_shift) 140 | right_part = right_part_tmp 141 | tempvar range_check_ptr = range_check_ptr 142 | end 143 | else: 144 | let (local right_part_tmp) = bitshift_right(rlp.element[end_word], 8 * right_shift) 145 | right_part = right_part_tmp 146 | tempvar range_check_ptr = range_check_ptr 147 | end 148 | 149 | local final_word = left_part + right_part 150 | let (local final_word_shifted) = bitshift_right(final_word, 8 * (8 - remainder)) 151 | 152 | let (local divider: felt) = pow(2, remainder * 8) 153 | let (_, final_word_masked) = unsigned_div_rem(final_word_shifted, divider) 154 | assert new_words[new_words_len] = final_word_masked 155 | else: 156 | local final_word_shifted 157 | if end_word == rlp.element_size_words - 1: 158 | let (local final_word_shifted_tmp) = bitshift_right(rlp.element[end_word], 8 * (last_rlp_word_len - end_pos)) 159 | final_word_shifted = final_word_shifted_tmp 160 | tempvar range_check_ptr = range_check_ptr 161 | else: 162 | let (local final_word_shifted_tmp) = bitshift_right(rlp.element[end_word], 8 * (8 - end_pos)) 163 | final_word_shifted = final_word_shifted_tmp 164 | tempvar range_check_ptr = range_check_ptr 165 | end 166 | 167 | let (local divider: felt) = pow(2, 8 * (end_pos - left_shift)) 168 | let (_, final_word_masked) = unsigned_div_rem(final_word_shifted, divider) 169 | assert new_words[new_words_len] = final_word_masked 170 | end 171 | result_words_len = new_words_len + 1 172 | tempvar range_check_ptr = range_check_ptr 173 | end 174 | 175 | local result: IntsSequence = IntsSequence(new_words, result_words_len, size) 176 | return (result) 177 | end 178 | 179 | func extract_data_rec{ range_check_ptr }( 180 | start_word: felt, 181 | full_words: felt, 182 | left_shift: felt, 183 | right_shift: felt, 184 | last_word_right_shift: felt, 185 | rlp: IntsSequence, 186 | acc: felt*, 187 | acc_len: felt, 188 | current_index: felt) -> (new_acc_size: felt): 189 | alloc_locals 190 | 191 | if current_index == full_words + start_word: 192 | return (acc_len) 193 | end 194 | 195 | let (local left_part) = bitshift_left(rlp.element[current_index], left_shift * 8) 196 | local right_part 197 | if current_index == rlp.element_size_words - 2: 198 | let (local is_last_word_right_shift_negative) = is_le(last_word_right_shift, -1) 199 | if is_last_word_right_shift_negative == 1: 200 | let (local right_part_tmp) = bitshift_left(rlp.element[current_index + 1], -8 * last_word_right_shift) 201 | right_part = right_part_tmp 202 | tempvar range_check_ptr = range_check_ptr 203 | else: 204 | let (local right_part_tmp) = bitshift_right(rlp.element[current_index + 1], 8 * last_word_right_shift) 205 | right_part = right_part_tmp 206 | tempvar range_check_ptr = range_check_ptr 207 | end 208 | tempvar range_check_ptr = range_check_ptr 209 | else: 210 | if current_index == rlp.element_size_words - 1: 211 | right_part = 0 212 | tempvar range_check_ptr = range_check_ptr 213 | else: 214 | let (local right_part_tmp) = bitshift_right(rlp.element[current_index + 1], 8 * right_shift) 215 | right_part = right_part_tmp 216 | tempvar range_check_ptr = range_check_ptr 217 | end 218 | tempvar range_check_ptr = range_check_ptr 219 | end 220 | 221 | local new_word = left_part + right_part 222 | let (local divider: felt) = pow(2, 64) 223 | 224 | local range_check_ptr = range_check_ptr 225 | 226 | let (_, new_word_masked) = unsigned_div_rem(new_word, divider) 227 | 228 | local range_check_ptr = range_check_ptr 229 | 230 | assert acc[current_index - start_word] = new_word_masked 231 | 232 | return extract_data_rec( 233 | start_word=start_word, 234 | full_words=full_words, 235 | left_shift=left_shift, 236 | right_shift=right_shift, 237 | last_word_right_shift=last_word_right_shift, 238 | rlp=rlp, 239 | acc=acc, 240 | acc_len=acc_len + 1, 241 | current_index=current_index + 1) 242 | end 243 | 244 | func is_rlp_list{ range_check_ptr }(pos: felt, rlp: IntsSequence) -> (res: felt): 245 | alloc_locals 246 | let (local data: IntsSequence) = extract_data(pos, 1, rlp) 247 | let (is_list) = is_le(191, data.element[0]) 248 | return (is_list) 249 | end 250 | 251 | func is_rlp_list_rlp_item{ range_check_ptr }(item: RLPItem, rlp: IntsSequence) -> (res: felt): 252 | alloc_locals 253 | local firstByte = item.firstByte 254 | let (is_list) = is_le(191, firstByte) 255 | return (is_list) 256 | end 257 | 258 | func to_list{ range_check_ptr }(rlp: IntsSequence) -> (items: RLPItem*, items_len: felt): 259 | alloc_locals 260 | 261 | let (is_list) = is_rlp_list(0, rlp) 262 | assert is_list = 1 263 | 264 | let (local payload: RLPItem) = getElement(rlp, 0) 265 | 266 | local payload_pos = payload.dataPosition 267 | local payload_len = payload.length 268 | 269 | let payload_end = payload_pos + payload_len 270 | let next_element_pos = payload_pos 271 | 272 | let (local items : RLPItem*) = alloc() 273 | let (items_len) = to_list_recursive(rlp, next_element_pos, payload_end, 0, items, 0) 274 | return (items, items_len) 275 | end 276 | 277 | func to_list_recursive{ range_check_ptr }( 278 | rlp: IntsSequence, 279 | next_element_pos: felt, 280 | payload_end: felt, 281 | current_index: felt, 282 | accumulator: RLPItem*, 283 | accumulator_len: felt) -> (res: felt): 284 | alloc_locals 285 | 286 | let (break) = is_le(payload_end, next_element_pos) 287 | if break == 1: 288 | return (current_index) 289 | end 290 | 291 | let (local payload: RLPItem) = getElement(rlp, next_element_pos) 292 | 293 | local payload_firstByte = payload.firstByte 294 | local payload_pos = payload.dataPosition 295 | local payload_len = payload.length 296 | 297 | assert accumulator[current_index] = RLPItem(payload_firstByte, payload_pos, payload_len) 298 | return to_list_recursive( 299 | rlp=rlp, 300 | next_element_pos=payload_pos + payload_len, 301 | payload_end=payload_end, 302 | current_index=current_index+1, 303 | accumulator=accumulator, 304 | accumulator_len=accumulator_len+1 305 | ) 306 | end 307 | 308 | func extract_list_values{ range_check_ptr }(rlp: IntsSequence, rlp_items: RLPItem*, rlp_items_len: felt) -> (res: IntsSequence*, res_len: felt): 309 | alloc_locals 310 | let (local acc: IntsSequence*) = alloc() 311 | extract_list_values_recursive(rlp, rlp_items, rlp_items_len, acc, 0, 0) 312 | return (acc, rlp_items_len) 313 | end 314 | 315 | func extract_list_values_recursive{ range_check_ptr }( 316 | rlp: IntsSequence, 317 | rlp_items: RLPItem*, 318 | rlp_items_len: felt, 319 | acc: IntsSequence*, 320 | acc_len: felt, 321 | current_index: felt): 322 | alloc_locals 323 | if current_index == rlp_items_len: 324 | return () 325 | end 326 | 327 | let (local data: IntsSequence) = extract_data(rlp_items[current_index].dataPosition, rlp_items[current_index].length, rlp) 328 | assert acc[current_index] = data 329 | 330 | return extract_list_values_recursive( 331 | rlp=rlp, 332 | rlp_items=rlp_items, 333 | rlp_items_len=rlp_items_len, 334 | acc=acc, 335 | acc_len=acc_len + 1, 336 | current_index=current_index + 1) 337 | end 338 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/keccak.cairo: -------------------------------------------------------------------------------- 1 | from lib.storage_verification.packed_keccak import BLOCK_SIZE, packed_keccak_func 2 | from lib.storage_verification.xor_state import state_xor, mask_garbage 3 | from lib.storage_verification.swap_endianness import swap_endianness_64 4 | 5 | from starkware.cairo.common.alloc import alloc 6 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 7 | from starkware.cairo.common.math import assert_nn_le, unsigned_div_rem 8 | from starkware.cairo.common.math_cmp import is_le 9 | from starkware.cairo.common.memset import memset 10 | from starkware.cairo.common.pow import pow 11 | 12 | # Runs keccak_f permutations on the given input 13 | # Uses packed_keccak_func from Starkware (in native Cairo) 14 | # Then 8 right bytes of each felt are extracted with a mask (everything to the left is considered garbage) 15 | func keccak_f{range_check_ptr, keccak_ptr : felt*, bitwise_ptr : BitwiseBuiltin*}(input : felt*) -> (output : felt*): 16 | let (garbaged_output) = packed_keccak_func(input) 17 | let (clean_output) = mask_garbage(garbaged_output) 18 | return (clean_output) 19 | end 20 | 21 | # Loads next keccak256 size block of words of the given inputs 22 | # 23 | # The input here is considered to be 136 bytes, so we can just copy it directly 24 | # The rest is filled with zero's to form a 200byte state 25 | func load_full_block{ bitwise_ptr : BitwiseBuiltin*, range_check_ptr, keccak_ptr_start: felt*, keccak_ptr : felt*}( 26 | input : felt*) -> (formatted_input : felt*): 27 | alloc_locals 28 | 29 | let (local swapped_input_0) = swap_endianness_64(input[0], 8) 30 | assert keccak_ptr[0] = swapped_input_0 31 | 32 | let (local swapped_input_1) = swap_endianness_64(input[1], 8) 33 | assert keccak_ptr[1] = swapped_input_1 34 | 35 | let (local swapped_input_2) = swap_endianness_64(input[2], 8) 36 | assert keccak_ptr[2] = swapped_input_2 37 | 38 | let (local swapped_input_3) = swap_endianness_64(input[3], 8) 39 | assert keccak_ptr[3] = swapped_input_3 40 | 41 | let (local swapped_input_4) = swap_endianness_64(input[4], 8) 42 | assert keccak_ptr[4] = swapped_input_4 43 | 44 | let (local swapped_input_5) = swap_endianness_64(input[5], 8) 45 | assert keccak_ptr[5] = swapped_input_5 46 | 47 | let (local swapped_input_6) = swap_endianness_64(input[6], 8) 48 | assert keccak_ptr[6] = swapped_input_6 49 | 50 | let (local swapped_input_7) = swap_endianness_64(input[7], 8) 51 | assert keccak_ptr[7] = swapped_input_7 52 | 53 | let (local swapped_input_8) = swap_endianness_64(input[8], 8) 54 | assert keccak_ptr[8] = swapped_input_8 55 | 56 | let (local swapped_input_9) = swap_endianness_64(input[9], 8) 57 | assert keccak_ptr[9] = swapped_input_9 58 | 59 | let (local swapped_input_10) = swap_endianness_64(input[10], 8) 60 | assert keccak_ptr[10] = swapped_input_10 61 | 62 | let (local swapped_input_11) = swap_endianness_64(input[11], 8) 63 | assert keccak_ptr[11] = swapped_input_11 64 | 65 | let (local swapped_input_12) = swap_endianness_64(input[12], 8) 66 | assert keccak_ptr[12] = swapped_input_12 67 | 68 | let (local swapped_input_13) = swap_endianness_64(input[13], 8) 69 | assert keccak_ptr[13] = swapped_input_13 70 | 71 | let (local swapped_input_14) = swap_endianness_64(input[14], 8) 72 | assert keccak_ptr[14] = swapped_input_14 73 | 74 | let (local swapped_input_15) = swap_endianness_64(input[15], 8) 75 | assert keccak_ptr[15] = swapped_input_15 76 | 77 | let (local swapped_input_16) = swap_endianness_64(input[16], 8) 78 | assert keccak_ptr[16] = swapped_input_16 79 | 80 | assert keccak_ptr[17] = 0 81 | assert keccak_ptr[18] = 0 82 | assert keccak_ptr[19] = 0 83 | assert keccak_ptr[20] = 0 84 | assert keccak_ptr[21] = 0 85 | assert keccak_ptr[22] = 0 86 | assert keccak_ptr[23] = 0 87 | assert keccak_ptr[24] = 0 88 | let keccak_ptr = keccak_ptr + 25 89 | 90 | return (keccak_ptr_start) 91 | end 92 | 93 | # Loads next keccak256 size block of words of the given inputs and applies additional padding 94 | # 95 | # In case the input is less than 136 bytes - padding rules must apply: 96 | # - input = 135bytes - 0x81 is added as a 136th byte (lsb) 97 | # - input < 135bytes - 0x80 is added as a 136th byte and 0x01 is added after the end of data 98 | # - if the input is empty - 0x01 is added at the beginning of the block, and 0x80 at 136th position 99 | # 100 | # the rest is filled with zeroes to form a 200byte state 101 | # 102 | # The function works recursively on each 64bit word. 103 | # Initially it's fed with n_word = 17, which is an inverse index (for recursion purposes) 104 | # Based on the n_word we can determine current index in the block: 105 | # - If n_word = 17 it means the current word is the first 106 | # - If n_word = 1 it means the current word is the last 107 | # - If n_word = 0 it means we already processed the data in the block and need to add capacity padding zeroes (8 words of them) 108 | 109 | func load_block_with_padding{ bitwise_ptr : BitwiseBuiltin*, range_check_ptr, keccak_ptr_start: felt*, keccak_ptr : felt*}( 110 | input : felt*, n_bytes : felt, n_word : felt) -> (formatted_input : felt*): 111 | alloc_locals 112 | 113 | # Actually we are checking if n_bytes is less than 8 114 | # n_bytes < 8: is_full_word = 0 115 | # n_bytes >= 8: is_full_word != 0 116 | # TODO: Can't we just use is_le() here? 117 | let (is_full_word, _) = unsigned_div_rem(n_bytes, 8) 118 | 119 | # If the current word is full (8 bytes, 64 bits) - we just copy it to the state 120 | if is_full_word != 0: 121 | let (local swapped_input_0) = swap_endianness_64(input[0], 8) 122 | assert keccak_ptr[0] = swapped_input_0 123 | 124 | let keccak_ptr = keccak_ptr + 1 125 | 126 | load_block_with_padding(input=input + 1, n_bytes=n_bytes - 8, n_word=n_word - 1) 127 | return (keccak_ptr_start) 128 | # Else, if the word is less than 8 bytes - we consider to add padding 129 | else: 130 | local final_padding 131 | 132 | # If it is the last word 133 | if n_word == 1: 134 | assert final_padding = 2 * 2 ** 62 # Add a padding 0x80 00 00 00 00 00 00 00 135 | else: 136 | assert final_padding = 0 # No 0x80 padding will be added 137 | end 138 | 139 | assert_nn_le(n_bytes, 7) 140 | let (padding) = pow(256, n_bytes) # This adds a 0x01 based on how many input bytes are left, straight to the left of them 141 | local range_check_ptr = range_check_ptr 142 | 143 | # If there are no bytes, then we just add the 0x01 padding to the right (and 0x80 to the left) 144 | if n_bytes == 0: 145 | if n_word != 0: 146 | assert keccak_ptr[0] = 1 + final_padding 147 | end 148 | tempvar bitwise_ptr = bitwise_ptr 149 | tempvar range_check_ptr = range_check_ptr 150 | # If there is some input data left in current word, we add 0x01 and 0x80 paddings to the left of the data 151 | else: 152 | let (local swapped_input_0) = swap_endianness_64(input[0], n_bytes) 153 | assert keccak_ptr[0] = swapped_input_0 + padding + final_padding 154 | tempvar bitwise_ptr = bitwise_ptr 155 | tempvar range_check_ptr = range_check_ptr 156 | end 157 | 158 | local bitwise_ptr : BitwiseBuiltin* = bitwise_ptr 159 | local range_check_ptr = range_check_ptr 160 | 161 | # If the input data finished at the last word - we add 8 words of capacity zeroes after it (64 bits) 162 | if n_word == 1: 163 | memset(dst=keccak_ptr + 1, value=0, n=n_word - 1 + 8) 164 | 165 | let keccak_ptr = keccak_ptr + n_word + 8 166 | return (keccak_ptr_start) 167 | # If the input data finished earlier than the 17th word: 168 | else: 169 | # Fill with zeroes until the last 17th word 170 | memset(dst=keccak_ptr + 1, value=0, n=n_word - 2) 171 | let keccak_ptr = keccak_ptr + n_word - 1 172 | 173 | # Insert a 0x80 00 00 00 00 00 00 00 padding at the 17th word 174 | assert keccak_ptr[0] = 2 * 2 ** 62 175 | # Fill the rest 8 words of capacity section with zeroes 176 | memset(dst=keccak_ptr + 1, value=0, n=8) 177 | return (keccak_ptr_start) 178 | end 179 | end 180 | end 181 | 182 | # Recursively runs the keccak256 algorithm, processing the imput block by block 183 | # Block size is fixed as 25 words (1600 bits), 184 | # in which 17 words (1088 bits) represent data 185 | # 186 | # Last block of data is being padded 187 | # 188 | # Each iteration consists of loading/formatting the data for the block 189 | # xorring the block with previous state 190 | # and performind a keccak permutation on that xor 191 | func recursive_keccak{range_check_ptr, keccak_ptr : felt*, bitwise_ptr : BitwiseBuiltin*}(state: felt*, input : felt*, n_bytes : felt) -> (output : felt*): 192 | alloc_locals 193 | let (is_n_bytes_above_136) = is_le(n_bytes, 136) 194 | local n_bytes_in_current_block 195 | if is_n_bytes_above_136 == 0: 196 | assert n_bytes_in_current_block = 136 197 | else: 198 | assert n_bytes_in_current_block = n_bytes 199 | end 200 | 201 | # If current input block is full (136 bytes, 1088 bits) - we use an optimized loader 202 | if n_bytes_in_current_block == 136: 203 | let (formatted_input: felt *) = load_full_block{keccak_ptr_start=keccak_ptr}(input=input) 204 | let (xor: felt*) = state_xor(state, formatted_input) 205 | let (keccak_f_ptr: felt*) = keccak_f(input=xor) 206 | let (state_update: felt*) = recursive_keccak(state=keccak_f_ptr, input=input+17, n_bytes=n_bytes-n_bytes_in_current_block) 207 | return (state_update) 208 | # For any block that is less than 136 bytes - the data is padded by keccak256 standard. 209 | # In case all previous blocks were perfectly 136 bytes - the last iteration should be perfomed on an empty block, also padded 210 | else: 211 | let (formatted_input: felt *) = load_block_with_padding{keccak_ptr_start=keccak_ptr}(input=input, n_bytes=n_bytes_in_current_block, n_word=17) 212 | let (xor: felt*) = state_xor(state, formatted_input) 213 | let (keccak_f_ptr: felt*) = keccak_f(input=xor) 214 | return (keccak_f_ptr) 215 | end 216 | end 217 | 218 | 219 | # Computes the keccak256 of 'input'. Inputs of any size are supported. 220 | # To use this function, split the input into words of 64 bits (little endian). 221 | # For example, to compute keccak('Hello world!'), use: 222 | # input = [8031924123371070792, 560229490] 223 | # where: 224 | # 8031924123371070792 == int.from_bytes(b'Hello wo', 'little') 225 | # 560229490 == int.from_bytes(b'rld!', 'little') 226 | # 227 | # output is an array of 4 64-bit words (little endian). 228 | # 229 | # 230 | func keccak256{range_check_ptr, keccak_ptr : felt*, bitwise_ptr : BitwiseBuiltin*}(input : felt*, n_bytes : felt) -> (output : felt*): 231 | let keccak_ptr_start = keccak_ptr 232 | alloc_locals 233 | 234 | # Allocates an empty felt array which will represent initial zeroed state 235 | let (local state : felt*) = alloc() 236 | 237 | # Fill state with 25 zeros 238 | assert state[0] = 0 239 | assert state[1] = 0 240 | assert state[2] = 0 241 | assert state[3] = 0 242 | assert state[4] = 0 243 | assert state[5] = 0 244 | assert state[6] = 0 245 | assert state[7] = 0 246 | assert state[8] = 0 247 | assert state[9] = 0 248 | assert state[10] = 0 249 | assert state[11] = 0 250 | assert state[12] = 0 251 | assert state[13] = 0 252 | assert state[14] = 0 253 | assert state[15] = 0 254 | assert state[16] = 0 255 | assert state[17] = 0 256 | assert state[18] = 0 257 | assert state[19] = 0 258 | assert state[20] = 0 259 | assert state[21] = 0 260 | assert state[22] = 0 261 | assert state[23] = 0 262 | assert state[24] = 0 263 | 264 | # Run keccak recursively 265 | let (output: felt*) = recursive_keccak(state=state, input=input, n_bytes=n_bytes) 266 | return (output) 267 | end 268 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/pow.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.math import unsigned_div_rem 2 | 3 | 4 | func pow{ range_check_ptr }(base: felt, p: felt) -> (res: felt): 5 | if p == 0: 6 | return (1) 7 | end 8 | 9 | let (accumulator) = pow(base=base, p=p-1) 10 | return (base * accumulator) 11 | end -------------------------------------------------------------------------------- /programs/lib/storage_verification/swap_endianness.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 2 | from starkware.cairo.common.math import assert_le 3 | from starkware.cairo.common.alloc import alloc 4 | from starkware.cairo.common.math import assert_nn_le, unsigned_div_rem 5 | from starkware.cairo.common.math_cmp import is_in_range 6 | from lib.storage_verification.pow import pow 7 | from lib.storage_verification.types import IntsSequence 8 | 9 | # func swap_endianness_many_words{ range_check_ptr, bitwise_ptr : BitwiseBuiltin* }(input: IntsSequence) -> (output: IntsSequence): 10 | # alloc_locals 11 | # let (local acc : felt*) = alloc() 12 | # swap_endianness_many_words_rec(input, acc, 0) 13 | # local res: IntsSequence = IntsSequence(acc, input.element_size_words, input.element_size_bytes) 14 | # return (res) 15 | # end 16 | 17 | func swap_endianness_four_words{ range_check_ptr, bitwise_ptr : BitwiseBuiltin* }(input: IntsSequence) -> (output: IntsSequence): 18 | alloc_locals 19 | let (local acc : felt*) = alloc() 20 | 21 | let (local swapped_input_0) = swap_endianness_64(input.element[0], 8) 22 | assert acc[0] = swapped_input_0 23 | 24 | let (local swapped_input_1) = swap_endianness_64(input.element[1], 8) 25 | assert acc[1] = swapped_input_1 26 | 27 | let (local swapped_input_2) = swap_endianness_64(input.element[2], 8) 28 | assert acc[2] = swapped_input_2 29 | 30 | let (local swapped_input_3) = swap_endianness_64(input.element[3], 8) 31 | assert acc[3] = swapped_input_3 32 | 33 | local res: IntsSequence = IntsSequence(acc, input.element_size_words, input.element_size_bytes) 34 | return (res) 35 | end 36 | 37 | # func swap_endianness_many_words_rec{ range_check_ptr, bitwise_ptr : BitwiseBuiltin* }( 38 | # input: IntsSequence, 39 | # acc: felt*, 40 | # current_word: felt): 41 | # alloc_locals 42 | 43 | # if current_word == input.element_size_words: 44 | # return () 45 | # end 46 | 47 | # # Is 0 when it is last word 48 | # let is_last_word = input.element_size_words - current_word - 1 49 | # local proper_len 50 | 51 | # if is_last_word == 0: 52 | # let (_, last_word_len) = unsigned_div_rem(input.element_size_bytes, 8) 53 | 54 | # if last_word_len == 0: 55 | # proper_len = 8 56 | # else: 57 | # proper_len = last_word_len 58 | # end 59 | # else: 60 | # proper_len = 8 61 | # end 62 | 63 | # let swapped: felt = swap_endianness_64(input.element[current_word], proper_len) 64 | # assert acc[current_word] = swapped 65 | # return swap_endianness_many_words_rec(input, acc, current_word + 1) 66 | # end 67 | 68 | func swap_endianness_64{ range_check_ptr, bitwise_ptr : BitwiseBuiltin* }(input: felt, size: felt) -> (output: felt): 69 | alloc_locals 70 | let (local output : felt*) = alloc() 71 | 72 | # verifies word fits in 64bits 73 | assert_le(input, 2**64 - 1) 74 | 75 | # swapped_bytes = ((word & 0xFF00FF00FF00FF00) >> 8) | ((word & 0x00FF00FF00FF00FF) << 8) 76 | let (left_part, _) = unsigned_div_rem(input, 256) 77 | 78 | assert bitwise_ptr[0].x = left_part 79 | assert bitwise_ptr[0].y = 0x00FF00FF00FF00FF 80 | 81 | assert bitwise_ptr[1].x = input * 256 82 | assert bitwise_ptr[1].y = 0xFF00FF00FF00FF00 83 | 84 | let swapped_bytes = bitwise_ptr[0].x_and_y + bitwise_ptr[1].x_and_y 85 | 86 | # swapped_2byte_pair = ((swapped_bytes & 0xFFFF0000FFFF0000) >> 16) | ((swapped_bytes & 0x0000FFFF0000FFFF) << 16) 87 | let (left_part2, _) = unsigned_div_rem(swapped_bytes, 2**16) 88 | 89 | assert bitwise_ptr[2].x = left_part2 90 | assert bitwise_ptr[2].y = 0x0000FFFF0000FFFF 91 | 92 | assert bitwise_ptr[3].x = swapped_bytes * 2**16 93 | assert bitwise_ptr[3].y = 0xFFFF0000FFFF0000 94 | 95 | let swapped_2bytes = bitwise_ptr[2].x_and_y + bitwise_ptr[3].x_and_y 96 | 97 | # swapped_4byte_pair = (swapped_2byte_pair >> 32) | ((swapped_2byte_pair << 32) % 2**64) 98 | let (left_part4, _) = unsigned_div_rem(swapped_2bytes, 2**32) 99 | 100 | assert bitwise_ptr[4].x = swapped_2bytes * 2**32 101 | assert bitwise_ptr[4].y = 0xFFFFFFFF00000000 102 | 103 | let swapped_4bytes = left_part4 + bitwise_ptr[4].x_and_y 104 | 105 | let bitwise_ptr = bitwise_ptr + 5 * BitwiseBuiltin.SIZE 106 | 107 | # Some Shiva-inspired code here 108 | let (local shift) = pow(2, ((8 - size) * 8)) 109 | 110 | if size == 8: 111 | return (swapped_4bytes) 112 | else: 113 | let (shifted_4bytes, _) = unsigned_div_rem(swapped_4bytes, shift) 114 | return (shifted_4bytes) 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/trie_proofs.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 2 | from starkware.cairo.common.math_cmp import is_le 3 | from starkware.cairo.common.alloc import alloc 4 | 5 | from lib.storage_verification.extract_from_rlp import extract_data, to_list, is_rlp_list, is_rlp_list_rlp_item 6 | from lib.storage_verification.words64 import extract_nibble, extract_nibble_from_words 7 | from lib.storage_verification.keccak import keccak256 8 | from lib.storage_verification.comp_arr import arr_eq 9 | from lib.storage_verification.swap_endianness import swap_endianness_four_words 10 | 11 | from lib.storage_verification.types import Keccak256Hash, IntsSequence, RLPItem 12 | 13 | # TODO check for safety 14 | func is_empty_keccak(input: IntsSequence) -> (res: felt): 15 | let not_empty = (input.element[0] - 6262289465969759654) * (input.element[1] - 18411636558227634286) * (input.element[2] - 6577753664917384640) * (input.element[3] - 99694600006120481) 16 | if not_empty == 0: 17 | return (1) 18 | else: 19 | return (0) 20 | end 21 | end 22 | 23 | 24 | func count_shared_prefix_len{ range_check_ptr }( 25 | path_offset: felt, 26 | path: IntsSequence, 27 | element_rlp: IntsSequence, 28 | node_path_item: RLPItem) -> (new_path_offset: felt): 29 | alloc_locals 30 | 31 | let (local node_path_decoded: IntsSequence) = extract_data( 32 | node_path_item.dataPosition, 33 | node_path_item.length, 34 | element_rlp) 35 | 36 | # TODO assert input_decoded len > 0 37 | 38 | # Extract node_path 39 | # Assumption that the first word of the proof element will be always a full word(8 bytes) 40 | let (first_nibble) = extract_nibble(node_path_decoded.element[0], 8, 0) 41 | 42 | local skip_nibbles 43 | 44 | if first_nibble == 0: 45 | skip_nibbles = 2 46 | else: 47 | if first_nibble == 1: 48 | skip_nibbles = 1 49 | else: 50 | if first_nibble == 2: 51 | skip_nibbles = 2 52 | else: 53 | if first_nibble == 3: 54 | skip_nibbles = 1 55 | else: 56 | assert 1 = 0 57 | end 58 | end 59 | end 60 | end 61 | 62 | let (local skipped_nibbles_above_size) = is_le(element_rlp.element_size_bytes, skip_nibbles) 63 | if skipped_nibbles_above_size == 1: 64 | return (path_offset) 65 | end 66 | 67 | let (shared_prefix) = count_shared_prefix_len_rec(path_offset, path, node_path_decoded, skip_nibbles, 0) 68 | return (shared_prefix + path_offset) 69 | end 70 | 71 | func count_shared_prefix_len_rec{ range_check_ptr }( 72 | path_offset: felt, 73 | path: IntsSequence, 74 | node_path_decoded: IntsSequence, 75 | skip_nibbles: felt, 76 | current_index: felt) -> (res: felt): 77 | alloc_locals 78 | let node_path_nibbles_len = node_path_decoded.element_size_bytes * 2 - skip_nibbles 79 | let path_nibbles_len = path.element_size_bytes * 2 80 | 81 | # current_index + path_offset >= len(path) 82 | let (local path_completed) = is_le(path_nibbles_len, current_index + path_offset) 83 | # current_index >= len(node_path) 84 | let (local node_path_completed) = is_le(node_path_nibbles_len, current_index) 85 | 86 | if path_completed + node_path_completed == 2: 87 | return (current_index) 88 | end 89 | 90 | let (current_path_nibble) = extract_nibble_from_words(path, current_index + path_offset) 91 | let (current_node_path_nibble) = extract_nibble_from_words(node_path_decoded, current_index + skip_nibbles) 92 | 93 | if current_path_nibble == current_node_path_nibble: 94 | return count_shared_prefix_len_rec(path_offset, path, node_path_decoded, skip_nibbles, current_index + 1) 95 | else: 96 | return (current_index) 97 | end 98 | end 99 | 100 | func get_next_hash{ range_check_ptr }(rlp_input: IntsSequence, rlp_node: RLPItem) -> (res: IntsSequence): 101 | alloc_locals 102 | assert rlp_node.length = 32 103 | let (local res: IntsSequence) = extract_data(rlp_node.dataPosition, rlp_node.length, rlp_input) 104 | assert res.element_size_words = 4 105 | return (res) 106 | end 107 | 108 | func verify_proof{ range_check_ptr, bitwise_ptr : BitwiseBuiltin* }( 109 | path: IntsSequence, 110 | root_hash: IntsSequence, 111 | proof: IntsSequence*, 112 | proof_len: felt) -> (res: IntsSequence): 113 | alloc_locals 114 | 115 | if proof_len == 0: 116 | let (is_root_zero) = is_empty_keccak(root_hash) 117 | assert is_root_zero = 1 118 | let (empty_arr) = alloc() 119 | local res: IntsSequence = IntsSequence(empty_arr, 0 , 0) 120 | return (res) 121 | end 122 | 123 | let (empty_arr) = alloc() 124 | local empty_hash: IntsSequence = IntsSequence(empty_arr, 0 , 0) 125 | 126 | return verify_proof_rec( 127 | path, 128 | root_hash, 129 | proof, 130 | proof_len, 131 | empty_hash, 132 | 0, 133 | 0) 134 | end 135 | 136 | 137 | func verify_proof_rec{ range_check_ptr, bitwise_ptr : BitwiseBuiltin* }( 138 | path: IntsSequence, 139 | root_hash: IntsSequence, 140 | proof: IntsSequence*, 141 | proof_len: felt, 142 | next_hash: IntsSequence, 143 | path_offset: felt, 144 | current_index: felt) -> (res: IntsSequence): 145 | alloc_locals 146 | 147 | let (local keccak_ptr : felt*) = alloc() 148 | let keccak_ptr_start = keccak_ptr 149 | 150 | if current_index == proof_len + 1: 151 | assert 1 = 0 152 | end 153 | 154 | local current_element: IntsSequence = proof[current_index] 155 | let (keccak_le_ptr) = keccak256{keccak_ptr=keccak_ptr}(current_element.element, current_element.element_size_bytes) 156 | local current_element_keccak_le: IntsSequence = IntsSequence(keccak_le_ptr, 4, 32) 157 | 158 | let (local current_element_keccak: IntsSequence) = swap_endianness_four_words(current_element_keccak_le) 159 | 160 | if current_index == 0: 161 | let (local hashes_match: felt) = arr_eq(current_element_keccak.element, current_element_keccak.element_size_words, root_hash.element, root_hash.element_size_words) 162 | assert hashes_match = 1 163 | else: 164 | let (local hashes_match: felt) = arr_eq(current_element_keccak.element, current_element_keccak.element_size_words, next_hash.element, next_hash.element_size_words) 165 | assert hashes_match = 1 166 | end 167 | 168 | let (node, node_len) = to_list(current_element) 169 | 170 | # Handle leaf node otherwise branch node 171 | if node_len == 2: 172 | # Leaf node logic goes here 173 | let (current_path_offset) = count_shared_prefix_len(path_offset, path, current_element, node[0]) 174 | if current_index == proof_len - 1: 175 | assert current_path_offset = path.element_size_bytes * 2 176 | let (local res: IntsSequence) = extract_data(node[1].dataPosition, node[1].length, current_element) 177 | return (res) 178 | else: 179 | local children: RLPItem = node[1] 180 | let (local is_list) = is_rlp_list_rlp_item(children, current_element) 181 | if is_list == 0: 182 | let (local next_hash: IntsSequence) = get_next_hash(current_element, children) 183 | return verify_proof_rec( 184 | path=path, 185 | root_hash=root_hash, 186 | proof=proof, 187 | proof_len=proof_len, 188 | next_hash=next_hash, 189 | path_offset=current_path_offset, 190 | current_index=current_index + 1) 191 | else: 192 | let (local element_data_extracted: IntsSequence) = extract_data(children.dataPosition, children.length, current_element) 193 | let (local next_hash_words_le: felt*) = keccak256{keccak_ptr=keccak_ptr}( 194 | element_data_extracted.element, 195 | element_data_extracted.element_size_bytes) 196 | 197 | local next_hash_le: IntsSequence = IntsSequence(next_hash_words_le, 4, 32) 198 | let (local next_hash: IntsSequence) = swap_endianness_four_words(next_hash_le) 199 | 200 | return verify_proof_rec( 201 | path=path, 202 | root_hash=root_hash, 203 | proof=proof, 204 | proof_len=proof_len, 205 | next_hash=next_hash, 206 | path_offset=current_path_offset, 207 | current_index=current_index + 1) 208 | end 209 | end 210 | else: 211 | # Branch node logic goes here 212 | assert node_len = 17 213 | 214 | if current_index == proof_len - 1: 215 | if path_offset + 1 == path.element_size_bytes * 2: 216 | let (local res: IntsSequence) = extract_data(node[16].dataPosition, node[16].length, current_element) 217 | return (res) 218 | else: 219 | let (local node_children: felt) = extract_nibble_from_words(path, path_offset) 220 | local children: RLPItem = node[node_children] 221 | assert children.length = 0 222 | let (empty_arr) = alloc() 223 | local res: IntsSequence = IntsSequence(empty_arr, 0 , 0) 224 | return (res) 225 | end 226 | else: 227 | let (not_reached_end) = is_le(path_offset, path.element_size_bytes * 2 - 1) 228 | assert not_reached_end = 1 229 | let (local node_children: felt) = extract_nibble_from_words(path, path_offset) 230 | local children: RLPItem = node[node_children] 231 | let current_path_offset = path_offset + 1 232 | 233 | let (local is_list) = is_rlp_list_rlp_item(children, current_element) 234 | 235 | if is_list == 0: 236 | let (local next_hash: IntsSequence) = get_next_hash(current_element, children) 237 | return verify_proof_rec( 238 | path=path, 239 | root_hash=root_hash, 240 | proof=proof, 241 | proof_len=proof_len, 242 | next_hash=next_hash, 243 | path_offset=current_path_offset, 244 | current_index=current_index + 1) 245 | else: 246 | let (local element_data_extracted: IntsSequence) = extract_data(children.dataPosition, children.length, current_element) 247 | 248 | let (local next_hash_words_le: felt*) = keccak256{keccak_ptr=keccak_ptr}( 249 | element_data_extracted.element, 250 | element_data_extracted.element_size_bytes) 251 | 252 | local next_hash_le: IntsSequence = IntsSequence(next_hash_words_le, 4, 32) 253 | let (local next_hash: IntsSequence) = swap_endianness_four_words(next_hash_le) 254 | 255 | return verify_proof_rec( 256 | path=path, 257 | root_hash=root_hash, 258 | proof=proof, 259 | proof_len=proof_len, 260 | next_hash=next_hash, 261 | path_offset=current_path_offset, 262 | current_index=current_index + 1) 263 | end 264 | end 265 | end 266 | assert 1 = 0 267 | ret 268 | end 269 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/types.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.alloc import alloc 2 | 3 | struct Keccak256Hash: 4 | member word_1 : felt 5 | member word_2 : felt 6 | member word_3 : felt 7 | member word_4 : felt 8 | end 9 | 10 | struct StorageSlot: 11 | member word_1 : felt 12 | member word_2 : felt 13 | member word_3 : felt 14 | member word_4 : felt 15 | end 16 | 17 | # first 2 words are full 18 | # the last word contains the remaining 4 bytes 19 | struct Address: 20 | member word_1 : felt 21 | member word_2 : felt 22 | member word_3 : felt 23 | end 24 | 25 | struct IntsSequence: 26 | member element : felt* 27 | member element_size_words: felt 28 | member element_size_bytes: felt 29 | end 30 | 31 | struct RLPItem: 32 | member firstByte : felt 33 | member dataPosition : felt 34 | member length : felt 35 | end 36 | 37 | ### IntsSequence utilities 38 | func reconstruct_ints_sequence_list{ range_check_ptr }( 39 | elements_concat: felt*, 40 | elements_concat_len: felt, 41 | elements_sizes_words: felt*, 42 | elements_sizes_words_len: felt, 43 | elements_sizes_bytes: felt*, 44 | elements_sizes_bytes_len: felt, 45 | acc: IntsSequence*, 46 | acc_len: felt, 47 | offset: felt, 48 | current_index: felt): 49 | alloc_locals 50 | 51 | if current_index == elements_sizes_words_len: 52 | return () 53 | end 54 | 55 | let (current_sequence_element_acc) = alloc() 56 | 57 | let (offset_updated) = slice_arr( 58 | offset, 59 | elements_sizes_words[current_index], 60 | elements_concat, 61 | elements_concat_len, 62 | current_sequence_element_acc, 63 | 0, 64 | 0) 65 | 66 | assert acc[current_index] = IntsSequence( 67 | current_sequence_element_acc, 68 | elements_sizes_words[current_index], 69 | elements_sizes_bytes[current_index]) 70 | 71 | 72 | return reconstruct_ints_sequence_list( 73 | elements_concat, 74 | elements_concat_len, 75 | elements_sizes_words, 76 | elements_sizes_words_len, 77 | elements_sizes_bytes, 78 | elements_sizes_bytes_len, 79 | acc, 80 | acc_len + 1, 81 | offset_updated, 82 | current_index + 1) 83 | end 84 | 85 | func slice_arr{ range_check_ptr }( 86 | start: felt, 87 | size: felt, 88 | arr: felt*, 89 | arr_len: felt, 90 | acc: felt*, 91 | acc_len: felt, 92 | current_index: felt) -> (offset: felt): 93 | if current_index == size: 94 | return (start + current_index) 95 | end 96 | 97 | assert acc[current_index] = arr[start + current_index] 98 | 99 | return slice_arr( 100 | start, 101 | size, 102 | arr, 103 | arr_len, 104 | acc, 105 | acc_len + 1, 106 | current_index + 1) 107 | end -------------------------------------------------------------------------------- /programs/lib/storage_verification/words64.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 2 | from starkware.cairo.common.math import assert_le, unsigned_div_rem 3 | from lib.storage_verification.bitshift import bitshift_right 4 | from lib.storage_verification.types import IntsSequence 5 | 6 | 7 | func extract_nibble{ range_check_ptr }(word: felt, word_len_bytes: felt, position: felt) -> (res: felt): 8 | assert_le(position, (word_len_bytes*2)-1) # Ensures that the extracted nibble is not out of word range 9 | let (shifted) = bitshift_right(word, (word_len_bytes*2-1) * 4 - position * 4) 10 | let (_, nibble) = unsigned_div_rem(shifted, 0x10) 11 | return (nibble) 12 | end 13 | 14 | func extract_nibble_from_words{ range_check_ptr }(input: IntsSequence, position: felt) -> (res: felt): 15 | alloc_locals 16 | let (word_index, nibble_index) = unsigned_div_rem(position, 16) 17 | 18 | # Is 0 when it is last word 19 | let is_last_word = input.element_size_words - word_index - 1 20 | 21 | if is_last_word == 0: 22 | let (_, last_word_len) = unsigned_div_rem(input.element_size_bytes, 8) 23 | local proper_len 24 | 25 | if last_word_len == 0: 26 | proper_len = 8 27 | else: 28 | proper_len = last_word_len 29 | end 30 | 31 | return extract_nibble(input.element[word_index], proper_len, nibble_index) 32 | else: 33 | return extract_nibble(input.element[word_index], 8, nibble_index) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /programs/lib/storage_verification/xor_state.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 2 | from starkware.cairo.common.bitwise import bitwise_xor 3 | from starkware.cairo.common.alloc import alloc 4 | 5 | # XORs the state with values 6 | func state_xor{bitwise_ptr : BitwiseBuiltin*}(state : felt*, values : felt*) -> (xored_values : felt*): 7 | alloc_locals 8 | let (local output : felt*) = alloc() 9 | 10 | assert bitwise_ptr[0].x = state[0] 11 | assert bitwise_ptr[1].x = state[1] 12 | assert bitwise_ptr[2].x = state[2] 13 | assert bitwise_ptr[3].x = state[3] 14 | assert bitwise_ptr[4].x = state[4] 15 | assert bitwise_ptr[5].x = state[5] 16 | assert bitwise_ptr[6].x = state[6] 17 | assert bitwise_ptr[7].x = state[7] 18 | assert bitwise_ptr[8].x = state[8] 19 | assert bitwise_ptr[9].x = state[9] 20 | assert bitwise_ptr[10].x = state[10] 21 | assert bitwise_ptr[11].x = state[11] 22 | assert bitwise_ptr[12].x = state[12] 23 | assert bitwise_ptr[13].x = state[13] 24 | assert bitwise_ptr[14].x = state[14] 25 | assert bitwise_ptr[15].x = state[15] 26 | assert bitwise_ptr[16].x = state[16] 27 | assert bitwise_ptr[17].x = state[17] 28 | assert bitwise_ptr[18].x = state[18] 29 | assert bitwise_ptr[19].x = state[19] 30 | assert bitwise_ptr[20].x = state[20] 31 | assert bitwise_ptr[21].x = state[21] 32 | assert bitwise_ptr[22].x = state[22] 33 | assert bitwise_ptr[23].x = state[23] 34 | assert bitwise_ptr[24].x = state[24] 35 | 36 | assert bitwise_ptr[0].y = values[0] 37 | assert bitwise_ptr[1].y = values[1] 38 | assert bitwise_ptr[2].y = values[2] 39 | assert bitwise_ptr[3].y = values[3] 40 | assert bitwise_ptr[4].y = values[4] 41 | assert bitwise_ptr[5].y = values[5] 42 | assert bitwise_ptr[6].y = values[6] 43 | assert bitwise_ptr[7].y = values[7] 44 | assert bitwise_ptr[8].y = values[8] 45 | assert bitwise_ptr[9].y = values[9] 46 | assert bitwise_ptr[10].y = values[10] 47 | assert bitwise_ptr[11].y = values[11] 48 | assert bitwise_ptr[12].y = values[12] 49 | assert bitwise_ptr[13].y = values[13] 50 | assert bitwise_ptr[14].y = values[14] 51 | assert bitwise_ptr[15].y = values[15] 52 | assert bitwise_ptr[16].y = values[16] 53 | assert bitwise_ptr[17].y = values[17] 54 | assert bitwise_ptr[18].y = values[18] 55 | assert bitwise_ptr[19].y = values[19] 56 | assert bitwise_ptr[20].y = values[20] 57 | assert bitwise_ptr[21].y = values[21] 58 | assert bitwise_ptr[22].y = values[22] 59 | assert bitwise_ptr[23].y = values[23] 60 | assert bitwise_ptr[24].y = values[24] 61 | 62 | assert output[0] = bitwise_ptr[0].x_xor_y 63 | assert output[1] = bitwise_ptr[1].x_xor_y 64 | assert output[2] = bitwise_ptr[2].x_xor_y 65 | assert output[3] = bitwise_ptr[3].x_xor_y 66 | assert output[4] = bitwise_ptr[4].x_xor_y 67 | assert output[5] = bitwise_ptr[5].x_xor_y 68 | assert output[6] = bitwise_ptr[6].x_xor_y 69 | assert output[7] = bitwise_ptr[7].x_xor_y 70 | assert output[8] = bitwise_ptr[8].x_xor_y 71 | assert output[9] = bitwise_ptr[9].x_xor_y 72 | assert output[10] = bitwise_ptr[10].x_xor_y 73 | assert output[11] = bitwise_ptr[11].x_xor_y 74 | assert output[12] = bitwise_ptr[12].x_xor_y 75 | assert output[13] = bitwise_ptr[13].x_xor_y 76 | assert output[14] = bitwise_ptr[14].x_xor_y 77 | assert output[15] = bitwise_ptr[15].x_xor_y 78 | assert output[16] = bitwise_ptr[16].x_xor_y 79 | assert output[17] = bitwise_ptr[17].x_xor_y 80 | assert output[18] = bitwise_ptr[18].x_xor_y 81 | assert output[19] = bitwise_ptr[19].x_xor_y 82 | assert output[20] = bitwise_ptr[20].x_xor_y 83 | assert output[21] = bitwise_ptr[21].x_xor_y 84 | assert output[22] = bitwise_ptr[22].x_xor_y 85 | assert output[23] = bitwise_ptr[23].x_xor_y 86 | assert output[24] = bitwise_ptr[24].x_xor_y 87 | 88 | let bitwise_ptr = bitwise_ptr + 25 * BitwiseBuiltin.SIZE 89 | return (output) 90 | end 91 | 92 | # Masks the 8 rightmost bytes on each felt 93 | # Everything to the left is considered garbage 94 | func mask_garbage{bitwise_ptr : BitwiseBuiltin*}(input : felt*) -> (output : felt*): 95 | alloc_locals 96 | let (local output : felt*) = alloc() 97 | 98 | assert bitwise_ptr[0].x = input[0] 99 | assert bitwise_ptr[1].x = input[1] 100 | assert bitwise_ptr[2].x = input[2] 101 | assert bitwise_ptr[3].x = input[3] 102 | assert bitwise_ptr[4].x = input[4] 103 | assert bitwise_ptr[5].x = input[5] 104 | assert bitwise_ptr[6].x = input[6] 105 | assert bitwise_ptr[7].x = input[7] 106 | assert bitwise_ptr[8].x = input[8] 107 | assert bitwise_ptr[9].x = input[9] 108 | assert bitwise_ptr[10].x = input[10] 109 | assert bitwise_ptr[11].x = input[11] 110 | assert bitwise_ptr[12].x = input[12] 111 | assert bitwise_ptr[13].x = input[13] 112 | assert bitwise_ptr[14].x = input[14] 113 | assert bitwise_ptr[15].x = input[15] 114 | assert bitwise_ptr[16].x = input[16] 115 | assert bitwise_ptr[17].x = input[17] 116 | assert bitwise_ptr[18].x = input[18] 117 | assert bitwise_ptr[19].x = input[19] 118 | assert bitwise_ptr[20].x = input[20] 119 | assert bitwise_ptr[21].x = input[21] 120 | assert bitwise_ptr[22].x = input[22] 121 | assert bitwise_ptr[23].x = input[23] 122 | assert bitwise_ptr[24].x = input[24] 123 | 124 | # e4 4c bb 43 3e b6 6a b9 125 | # we need 64bits 126 | # 2^64-1 127 | let mask = 2 ** 64 - 1 128 | 129 | assert bitwise_ptr[0].y = mask 130 | assert bitwise_ptr[1].y = mask 131 | assert bitwise_ptr[2].y = mask 132 | assert bitwise_ptr[3].y = mask 133 | assert bitwise_ptr[4].y = mask 134 | assert bitwise_ptr[5].y = mask 135 | assert bitwise_ptr[6].y = mask 136 | assert bitwise_ptr[7].y = mask 137 | assert bitwise_ptr[8].y = mask 138 | assert bitwise_ptr[9].y = mask 139 | assert bitwise_ptr[10].y = mask 140 | assert bitwise_ptr[11].y = mask 141 | assert bitwise_ptr[12].y = mask 142 | assert bitwise_ptr[13].y = mask 143 | assert bitwise_ptr[14].y = mask 144 | assert bitwise_ptr[15].y = mask 145 | assert bitwise_ptr[16].y = mask 146 | assert bitwise_ptr[17].y = mask 147 | assert bitwise_ptr[18].y = mask 148 | assert bitwise_ptr[19].y = mask 149 | assert bitwise_ptr[20].y = mask 150 | assert bitwise_ptr[21].y = mask 151 | assert bitwise_ptr[22].y = mask 152 | assert bitwise_ptr[23].y = mask 153 | assert bitwise_ptr[24].y = mask 154 | 155 | assert output[0] = bitwise_ptr[0].x_and_y 156 | assert output[1] = bitwise_ptr[1].x_and_y 157 | assert output[2] = bitwise_ptr[2].x_and_y 158 | assert output[3] = bitwise_ptr[3].x_and_y 159 | assert output[4] = bitwise_ptr[4].x_and_y 160 | assert output[5] = bitwise_ptr[5].x_and_y 161 | assert output[6] = bitwise_ptr[6].x_and_y 162 | assert output[7] = bitwise_ptr[7].x_and_y 163 | assert output[8] = bitwise_ptr[8].x_and_y 164 | assert output[9] = bitwise_ptr[9].x_and_y 165 | assert output[10] = bitwise_ptr[10].x_and_y 166 | assert output[11] = bitwise_ptr[11].x_and_y 167 | assert output[12] = bitwise_ptr[12].x_and_y 168 | assert output[13] = bitwise_ptr[13].x_and_y 169 | assert output[14] = bitwise_ptr[14].x_and_y 170 | assert output[15] = bitwise_ptr[15].x_and_y 171 | assert output[16] = bitwise_ptr[16].x_and_y 172 | assert output[17] = bitwise_ptr[17].x_and_y 173 | assert output[18] = bitwise_ptr[18].x_and_y 174 | assert output[19] = bitwise_ptr[19].x_and_y 175 | assert output[20] = bitwise_ptr[20].x_and_y 176 | assert output[21] = bitwise_ptr[21].x_and_y 177 | assert output[22] = bitwise_ptr[22].x_and_y 178 | assert output[23] = bitwise_ptr[23].x_and_y 179 | assert output[24] = bitwise_ptr[24].x_and_y 180 | 181 | let bitwise_ptr = bitwise_ptr + 25 * BitwiseBuiltin.SIZE 182 | return (output) 183 | end 184 | -------------------------------------------------------------------------------- /programs/storage_proof.cairo: -------------------------------------------------------------------------------- 1 | %builtins output range_check bitwise 2 | 3 | # Keccak code from https://github.com/starkware-libs/cairo-examples/tree/master/keccak 4 | from lib.keccak import keccak, finalize_keccak 5 | from lib.bytes_utils import IntArray, swap_endian 6 | 7 | # Storage verification code from https://github.com/OilerNetwork/fossil 8 | from lib.storage_verification.keccak import keccak256 9 | from lib.storage_verification.trie_proofs import verify_proof 10 | from lib.storage_verification.pow import pow 11 | from lib.storage_verification.extract_from_rlp import extract_data, extractElement 12 | from lib.storage_verification.types import IntsSequence, reconstruct_ints_sequence_list 13 | from lib.storage_verification.swap_endianness import swap_endianness_four_words 14 | from lib.storage_verification.comp_arr import arr_eq 15 | from lib.storage_verification.concat_arr import concat_arr 16 | 17 | from starkware.cairo.common.cairo_secp.bigint import BigInt3 18 | from starkware.cairo.common.cairo_secp.signature import recover_public_key, public_key_point_to_eth_address 19 | 20 | from starkware.cairo.common.uint256 import Uint256, uint256_unsigned_div_rem, split_64 21 | from starkware.cairo.common.registers import get_fp_and_pc 22 | from starkware.cairo.common.alloc import alloc 23 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 24 | from starkware.cairo.common.math import unsigned_div_rem, split_felt 25 | from starkware.cairo.common.math_cmp import is_le 26 | from starkware.cairo.common.serialize import serialize_word 27 | 28 | const ACCOUNT = 1 29 | const STORAGE = 2 30 | 31 | struct Signature: 32 | member message: IntArray 33 | member r: BigInt3 34 | member s: BigInt3 35 | member v: felt 36 | end 37 | 38 | struct StorageProof: 39 | member key: IntArray 40 | member value: IntArray 41 | member proof: IntsSequence* 42 | end 43 | 44 | struct Proof: 45 | member public_eth_address: felt 46 | member token_balance_min: felt 47 | member block_number: felt 48 | member account_address: felt 49 | member state_root: IntArray 50 | member storage_slot: IntArray 51 | member storage_hash: IntArray 52 | member storage_proof: StorageProof 53 | member storage_proof_len: felt 54 | member signature: Signature 55 | end 56 | 57 | func u256_to_bigint3{range_check_ptr}(a: Uint256) -> (res: BigInt3): 58 | alloc_locals 59 | local base : Uint256 = Uint256(low=2 ** 86, high=0) 60 | let (n0, d0) = uint256_unsigned_div_rem(a, base) 61 | let (n1, d1) = uint256_unsigned_div_rem(n0, base) 62 | let (_, d2) = uint256_unsigned_div_rem(n1, base) 63 | local res : BigInt3 = BigInt3(d0.low, d1.low, d2.low) 64 | return (res) 65 | end 66 | 67 | func felt_to_int_array{range_check_ptr}(a: felt) -> (res: felt*): 68 | alloc_locals 69 | let (hi, lo) = split_felt(a) 70 | let (local res : felt*) = alloc() 71 | let (r0, r1) = split_64(lo) 72 | let (r2, r3) = split_64(hi) 73 | assert res[0] = r3 74 | assert res[1] = r2 75 | assert res[2] = r1 76 | assert res[3] = r0 77 | return (res) 78 | end 79 | 80 | func int_array_to_felt(a: felt*, word_len: felt) -> (res: felt): 81 | if word_len == 1: 82 | return (a[0]) 83 | end 84 | if word_len == 2: 85 | return (a[1] + a[0]*2**64) 86 | end 87 | if word_len == 3: 88 | return (a[2] + a[1]*2**64 + a[0]*2**128) 89 | end 90 | if word_len == 4: 91 | return (a[3] + a[2]*2**64 + a[1]*2**128 + a[0]*2**192) 92 | end 93 | return (0) 94 | end 95 | 96 | func to_ints_seq(int_array: IntArray) -> (res: IntsSequence): 97 | let res = IntsSequence( 98 | element=int_array.elements, 99 | element_size_words=int_array.word_len, 100 | element_size_bytes=int_array.byte_len 101 | ) 102 | return (res) 103 | end 104 | 105 | # Slice to four 64 bit words, beginning from start_pos 106 | func slice_arr( 107 | array: felt*, 108 | start_pos: felt) -> (res: IntArray): 109 | alloc_locals 110 | let (elements : felt*) = alloc() 111 | assert elements[0] = array[start_pos] 112 | assert elements[1] = array[start_pos + 1] 113 | assert elements[2] = array[start_pos + 2] 114 | assert elements[3] = array[start_pos + 3] 115 | local res: IntArray = IntArray(elements=elements, word_len=4, byte_len=32) 116 | return (res) 117 | end 118 | 119 | func extract_message_contents(message: IntArray) -> ( 120 | eth_address: felt, storage_root: IntArray, storage_key: IntArray): 121 | alloc_locals 122 | let (eth_address) = int_array_to_felt(message.elements + 4, 4) 123 | let (storage_root) = slice_arr(message.elements, 8) 124 | let (storage_key) = slice_arr(message.elements, 12) 125 | return (eth_address, storage_root, storage_key) 126 | end 127 | 128 | func encode_kv_position{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}( 129 | slot: IntArray, address: IntArray) -> (key : IntsSequence): 130 | alloc_locals 131 | let (input : felt*) = alloc() 132 | 133 | # Address 134 | assert input[0] = address.elements[0] 135 | assert input[1] = address.elements[1] 136 | assert input[2] = address.elements[2] 137 | assert input[3] = address.elements[3] 138 | 139 | # Storage slot 140 | assert input[4] = slot.elements[0] 141 | assert input[5] = slot.elements[1] 142 | assert input[6] = slot.elements[2] 143 | assert input[7] = slot.elements[3] 144 | 145 | let (local keccak_ptr : felt*) = alloc() 146 | let (out_le) = keccak256{keccak_ptr=keccak_ptr}(input, 64) 147 | let (key) = swap_endianness_four_words(IntsSequence(out_le, 4, 32)) 148 | return (key) 149 | end 150 | 151 | func extract_verification_arguments(proof_ptr : Proof*, proof_type: felt) -> ( 152 | root_hash: IntsSequence, 153 | proof: IntsSequence*, 154 | proof_len: felt): 155 | alloc_locals 156 | if proof_type == STORAGE: 157 | let (root) = to_ints_seq(proof_ptr.storage_hash) 158 | local proof : IntsSequence* = proof_ptr.storage_proof.proof 159 | local len = proof_ptr.storage_proof_len 160 | return (root, proof, len) 161 | end 162 | ret 163 | end 164 | 165 | 166 | func hash_eip191_message{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}( 167 | message: IntArray) -> (msg_hash : BigInt3): 168 | alloc_locals 169 | let (local keccak_ptr : felt*) = alloc() 170 | let (output_le) = keccak256{keccak_ptr=keccak_ptr}(message.elements, message.byte_len) 171 | let (output_be) = swap_endianness_four_words(IntsSequence(output_le, 4, 32)) 172 | local output : felt* = output_be.element 173 | local output_uint : Uint256 = Uint256( 174 | low=output[2]*2**64 + output[3], 175 | high=output[0]*2**64 + output[1]) 176 | let (msg_hash) = u256_to_bigint3(output_uint) 177 | return (msg_hash) 178 | end 179 | 180 | func recover_address{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}( 181 | msg_hash: BigInt3, r: BigInt3, s: BigInt3, v: felt) -> (address: IntArray): 182 | alloc_locals 183 | 184 | let (public_key_ec) = recover_public_key(msg_hash, r, s, v-27) 185 | 186 | let (local keccak_ptr : felt*) = alloc() 187 | let keccak_ptr_start = keccak_ptr 188 | let (calculated_eth_address : felt) = public_key_point_to_eth_address{ 189 | keccak_ptr=keccak_ptr}(public_key_ec) 190 | finalize_keccak(keccak_ptr_start=keccak_ptr_start, keccak_ptr_end=keccak_ptr) 191 | 192 | # Convert felt address to IntArray 193 | let (local elements : felt*) = felt_to_int_array(calculated_eth_address) 194 | local address : IntArray = IntArray(elements, 4, 32) 195 | 196 | return (address) 197 | end 198 | 199 | func verify_storage_proof{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}( 200 | proof_ptr : Proof*, 201 | public_eth_address : felt, 202 | private_eth_address : IntArray, 203 | token_balance_min : felt): 204 | alloc_locals 205 | 206 | # Extract public eth address, storage root, and storage key from signed message contents 207 | let message = proof_ptr.signature.message 208 | let (signed_public_eth_address, state_root, storage_key) = extract_message_contents(message) 209 | 210 | # Verify that the claimed public address is in the signed message 211 | assert signed_public_eth_address = public_eth_address 212 | 213 | # Verify that the signed storage key matches derived trie key 214 | let storage_slot = proof_ptr.storage_slot 215 | let (path) = encode_kv_position(storage_slot, private_eth_address) 216 | 217 | # Compare trie path to signed storage key 218 | let (local storage_key_match: felt) = arr_eq( 219 | path.element, path.element_size_words, 220 | storage_key.elements, storage_key.word_len) 221 | assert storage_key_match = 1 222 | 223 | # Retrieve RLP encoded storage value from proof 224 | let (root_hash, proof, proof_len) = extract_verification_arguments(proof_ptr, STORAGE) 225 | let (local keccak_ptr : felt*) = alloc() 226 | let (path_hash_le) = keccak256{keccak_ptr=keccak_ptr}(path.element, 32) 227 | let (path_hash_be) = swap_endianness_four_words(IntsSequence(path_hash_le, 4, 32)) 228 | let (local rlp_value: IntsSequence) = verify_proof(path_hash_be, root_hash, proof, proof_len) 229 | 230 | # Decode storage value 231 | let (local data: IntsSequence) = extract_data{ range_check_ptr = range_check_ptr }(0, 1, rlp_value) 232 | let storage_value_len = data.element[0] - 128 233 | let (storage_value) = extractElement(rlp_value, 0) 234 | 235 | # Convert storage value to integer representation 236 | # Note: this assumes that balance_raw < 2**128 237 | let (balance_raw) = int_array_to_felt(storage_value.element, storage_value.element_size_words) 238 | let (den) = pow(2, (32 - 2*storage_value_len)*4) 239 | let balance = balance_raw / den 240 | 241 | # Check that balance in storage > minimum 242 | let (res) = is_le(token_balance_min, balance) 243 | assert res = 1 244 | 245 | return () 246 | end 247 | 248 | 249 | func encode_proof_from_json() -> (proof : Proof*): 250 | alloc_locals 251 | 252 | local public_eth_address : felt 253 | local token_balance_min : felt 254 | local block_number : felt 255 | 256 | local storage_proof_len : felt 257 | local account_address : felt 258 | local state_root : IntArray 259 | local storage_slot : IntArray 260 | local storage_hash : IntArray 261 | 262 | local message : IntArray 263 | local r : BigInt3 264 | local s : BigInt3 265 | local v : felt 266 | 267 | let (storage_proof0 : IntsSequence*) = alloc() 268 | 269 | local storage_key : IntArray 270 | local storage_value : IntArray 271 | 272 | %{ 273 | from math import ceil 274 | from starkware.cairo.common.cairo_secp.secp_utils import split 275 | 276 | def pack_intarray(base_addr, hex_input): 277 | elements = segments.add() 278 | for j in range(0, len(hex_input) // 16 + 1): 279 | hex_str = hex_input[j*16 : (j+1) * 16] 280 | if len(hex_str) > 0: 281 | memory[elements + j] = int(hex_str, 16) 282 | memory[base_addr + ids.IntArray.elements] = elements 283 | memory[base_addr + ids.IntArray.word_len] = int(ceil(len(hex_input) / 2. / 8)) 284 | memory[base_addr + ids.IntArray.byte_len] = int(len(hex_input) / 2) 285 | 286 | def pack_bigint3(base_addr, input): 287 | d0, d1, d2 = split(input) 288 | memory[base_addr + ids.BigInt3.d0] = d0 289 | memory[base_addr + ids.BigInt3.d1] = d1 290 | memory[base_addr + ids.BigInt3.d2] = d2 291 | 292 | ids.storage_proof_len = len(program_input['storageProof'][0]['proof']) 293 | 294 | pack_intarray(ids.state_root.address_, program_input['stateRoot'][2:]) 295 | pack_intarray(ids.storage_slot.address_, program_input['storageSlot'][2:]) 296 | pack_intarray(ids.storage_hash.address_, program_input['storageHash'][2:]) 297 | pack_intarray(ids.storage_key.address_, program_input['storageProof'][0]['key'][2:]) 298 | pack_intarray(ids.storage_value.address_, program_input['storageProof'][0]['value'][2:]) 299 | 300 | # Storage proof 301 | for i, proof in enumerate(program_input['storageProof'][0]['proof']): 302 | base_addr = ids.storage_proof0.address_ + ids.IntsSequence.SIZE * i 303 | pack_intarray(base_addr, proof[2:]) 304 | 305 | # Signature 306 | pack_intarray(ids.message.address_, program_input['signature']['message'][2:]) 307 | pack_bigint3(ids.r.address_, program_input['signature']['r']) 308 | pack_bigint3(ids.s.address_, program_input['signature']['s']) 309 | ids.v = program_input['signature']['v'] 310 | 311 | # Contract address 312 | ids.account_address = int(program_input['address'][2:], 16) 313 | 314 | # Public Ethereum address 315 | ids.public_eth_address = program_input['publicEthAddress'] 316 | 317 | # Minimum token balance 318 | ids.token_balance_min = program_input['tokenBalanceMin'] 319 | 320 | # Block number 321 | ids.block_number = program_input['blockNumber'] 322 | %} 323 | 324 | local storage_proof : StorageProof = StorageProof( 325 | key=storage_key, value=storage_value, proof=storage_proof0) 326 | local signature : Signature = Signature( 327 | message=message, r=r, s=s, v=v) 328 | local proof: Proof = Proof( 329 | public_eth_address=public_eth_address, 330 | token_balance_min=token_balance_min, 331 | block_number=block_number, 332 | account_address=account_address, 333 | state_root=state_root, 334 | storage_slot=storage_slot, 335 | storage_hash=storage_hash, 336 | storage_proof=storage_proof, 337 | storage_proof_len=storage_proof_len, 338 | signature=signature) 339 | 340 | let (__fp__, _) = get_fp_and_pc() 341 | return (proof=&proof) 342 | end 343 | 344 | func serialize_int_array{output_ptr : felt*}(int_array: IntArray): 345 | serialize_word(int_array.elements[0]) 346 | serialize_word(int_array.elements[1]) 347 | serialize_word(int_array.elements[2]) 348 | serialize_word(int_array.elements[3]) 349 | return () 350 | end 351 | 352 | func main{output_ptr : felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}(): 353 | alloc_locals 354 | let (local proof: Proof*) = encode_proof_from_json() 355 | 356 | # Extract Ethereum account address from signed message hash and signature 357 | let message = proof.signature.message 358 | let r = proof.signature.r 359 | let s = proof.signature.s 360 | let v = proof.signature.v 361 | let (msg_hash) = hash_eip191_message(message) 362 | let (private_eth_address) = recover_address(msg_hash, r, s, v) 363 | let token_balance_min = proof.token_balance_min 364 | 365 | let (__fp__, _) = get_fp_and_pc() 366 | 367 | verify_storage_proof( 368 | proof_ptr=proof, 369 | public_eth_address=proof.public_eth_address, 370 | private_eth_address=private_eth_address, 371 | token_balance_min=token_balance_min) 372 | 373 | # Serialize the following to output: 374 | # - Block number 375 | # - State root hash 376 | # - Storage root hash 377 | # - Account address 378 | # - Public ethereum address 379 | # - Minimum token balance threshold 380 | # 381 | # Note that block number, state root hash, and account are not checked in 382 | # this proof in order to keep the proving time and RAM requirements low. 383 | # The authenticity and relationship between these values can easily be 384 | # verified offline by downloading the required Merkle proofs from a node. 385 | 386 | serialize_word(proof.block_number) 387 | serialize_word(proof.account_address) 388 | serialize_word(proof.public_eth_address) 389 | serialize_word(proof.token_balance_min) 390 | serialize_int_array(proof.state_root) 391 | serialize_int_array(proof.storage_hash) 392 | 393 | return () 394 | end 395 | -------------------------------------------------------------------------------- /public/images/eth-diamond-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxgillett/stark-attestations/821e396959898a7c817689e92587840a6e3f5260/public/images/eth-diamond-black.png -------------------------------------------------------------------------------- /public/images/eth-diamond-glyph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxgillett/stark-attestations/821e396959898a7c817689e92587840a6e3f5260/public/images/eth-diamond-glyph.png -------------------------------------------------------------------------------- /public/images/eth-diamond-purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxgillett/stark-attestations/821e396959898a7c817689e92587840a6e3f5260/public/images/eth-diamond-purple.png -------------------------------------------------------------------------------- /public/images/eth-diamond-rainbow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxgillett/stark-attestations/821e396959898a7c817689e92587840a6e3f5260/public/images/eth-diamond-rainbow.webp -------------------------------------------------------------------------------- /public/images/milad-fakurian-iFu2HILEng8-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxgillett/stark-attestations/821e396959898a7c817689e92587840a6e3f5260/public/images/milad-fakurian-iFu2HILEng8-unsplash.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | React App 15 | 16 | 17 | 18 |
19 | 20 | 30 | 31 | 36 | 37 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Your Orders", 3 | "name": "Your Orders", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as Wasm from '../pkg'; 2 | import * as React from 'react'; 3 | import Button from '@mui/material/Button'; 4 | import { 5 | BrowserRouter as Router, 6 | Routes, 7 | Route 8 | } from 'react-router-dom'; 9 | 10 | import HomePage from "./pages/Home/HomePage" 11 | import MintPage from "./pages/Mint/MintPage" 12 | import ViewAccountPage from "./pages/Badges/ViewAccountPage" 13 | import ViewBadgePage from "./pages/Badges/ViewBadgePage" 14 | import Header from "./components/Header/Header" 15 | 16 | export default function App() { 17 | return ( 18 |
19 | 20 |
21 | 22 | }/> 23 | }/> 24 | }/> 25 | }/> 26 | 27 | 28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Badge/Badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as Wasm from '../../../pkg'; 3 | 4 | import { create } from 'ipfs-http-client'; 5 | import { concat as uint8ArrayConcat } from 'uint8arrays/concat' 6 | import all from 'it-all' 7 | import moment from "moment" 8 | 9 | import Web3 from 'web3'; 10 | import { hooks, metaMask } from '../../connectors/metaMask' 11 | import { GetAndVerify, GetProof, VerifyProof } from 'eth-proof'; 12 | import { toBuffer } from 'eth-util-lite'; 13 | 14 | import Card from '@mui/material/Card'; 15 | import CardActions from '@mui/material/CardActions'; 16 | import CardContent from '@mui/material/CardContent'; 17 | import CardMedia from '@mui/material/CardMedia'; 18 | import Button from '@mui/material/Button'; 19 | import Typography from '@mui/material/Typography'; 20 | 21 | import Table from '@mui/material/Table'; 22 | import TableBody from '@mui/material/TableBody'; 23 | import TableCell from '@mui/material/TableCell'; 24 | import TableContainer from '@mui/material/TableContainer'; 25 | import TableHead from '@mui/material/TableHead'; 26 | import TableRow from '@mui/material/TableRow'; 27 | 28 | import Paper from '@mui/material/Paper'; 29 | import Tooltip from '@mui/material/Tooltip'; 30 | import Avatar from '@mui/material/Avatar'; 31 | 32 | 33 | const tokenLookups = { 34 | '0xc18360217d8f7ab5e7c516566761ea12ce7f9d72': 'ENS (Ethereum Name Service)' 35 | } 36 | 37 | const ipfs = create({url: 'https://ipfs.infura.io:5001/api/v0'}); 38 | const getAndVerify = new GetAndVerify('http://cloudflare-eth.com/v1/mainnet') 39 | 40 | const { useChainId, useAccounts, useError, useIsActivating, useIsActive, useProvider, useENSNames } = hooks 41 | 42 | async function callIpfs(cid: any, setRes: any, setStatus: any) { 43 | const res = await ipfs.cat(cid, {length: 1_000_000}); 44 | (async () => { 45 | try { 46 | const response = uint8ArrayConcat(await all(res)) 47 | setStatus("loaded") 48 | setRes(response) 49 | } catch(e) { 50 | setStatus("failed") 51 | } 52 | })(); 53 | } 54 | 55 | function verifyProof(info, data, verified, setVerified, provider, isActive) { 56 | if (!isActive) { 57 | return 58 | } 59 | if (verified == "unknown") { 60 | (async () => { 61 | const blockNumber = "0x"+BigInt(info.block_number).toString(16); 62 | const accountAddress = "0x"+info.account_address.slice(-40) 63 | const stateRoot = "0x"+info.state_root // "0xe1a012879b33390bad6a24f8e7b16cf00a52e25587d3b1c90986e5bd37f8bf2b" 64 | const storageRoot = "0x"+info.storage_root // "0x1d6fee9790da714289ad12a51c30e03bb4aa5541c98811ea5334f57104f16a57" 65 | 66 | const block = await provider?.send("eth_getBlockByNumber", [blockNumber, false]); 67 | const blockHash = block.hash 68 | 69 | // Fetch account proof 70 | const accountProof = await provider?.send( 71 | "eth_getProof", [accountAddress, [], blockNumber]); 72 | 73 | // Verify account proof 74 | let stateRootFromProofRaw = VerifyProof.getRootFromProof(accountProof) 75 | let stateRootFromProof = Buffer.from(stateRootFromProofRaw).toString('hex'); 76 | const accountFromProofRaw = await VerifyProof.getAccountFromProofAt(accountProof, accountAddress) 77 | let storageRootFromProof = Buffer.from(accountFromProofRaw.storageRoot).toString('hex'); 78 | 79 | // Check that state trie root matches block 80 | // Check that contract account is in state trie 81 | // Check that storage trie root matches contract account 82 | // 83 | // TODO: The eth-proof library contains a bug. The correct roots are not being derived, so we 84 | // will assume that the node is providing the correct data. 85 | // 86 | //if (stateRoot != stateRootFromProof) throw new Error('State root mismatch') 87 | //if (storageRoot != storageRootFromProof) throw new Error('Storage root mismatch') 88 | if (stateRoot != block.stateRoot || storageRoot != accountProof.storageRoot) { 89 | setVerified("failed") 90 | } 91 | 92 | // Verify STARK proof 93 | (Wasm.verify(data)) ? setVerified("success") : setVerified("failed") 94 | })(); 95 | } 96 | } 97 | 98 | function extractProofDetails(data, provider, isActive, setTimeStamp) { 99 | const info = Wasm.get_info(data) 100 | console.log(info.block_number) 101 | console.log(info.account_address) 102 | console.log(info.public_eth_address) 103 | console.log(info.token_balance_min) 104 | console.log(info.state_root) 105 | console.log(info.storage_root) 106 | console.log(info.program_hash) 107 | console.log(info.security_level) 108 | 109 | if (isActive) { 110 | (async () => { 111 | const blockNumber = "0x"+BigInt(info.block_number).toString(16); 112 | const block = await provider?.send("eth_getBlockByNumber", [blockNumber, false]); 113 | setTimeStamp(BigInt(block.timestamp).toString(10)) 114 | })(); 115 | } 116 | 117 | return info 118 | } 119 | 120 | function getTokenName(rawAddress: string) { 121 | const address = rawAddress.toLowerCase() 122 | if (address in tokenLookups) { 123 | return tokenLookups[address] 124 | } else { 125 | return address 126 | } 127 | } 128 | 129 | 130 | interface IpfsObject { 131 | cid: String, 132 | } 133 | 134 | interface PublicInput { 135 | info: any 136 | timeStamp: string, 137 | rawData: Uint8Array, 138 | provider: any 139 | } 140 | 141 | interface RowInput { 142 | timeStamp: string, 143 | blockNumber: string; 144 | publicEthereumAddress: string, 145 | minTokenBalance: string; 146 | programHash: string; 147 | securityLevel: string 148 | } 149 | 150 | export default function BadgeViewer(props: IpfsObject) { 151 | const isActive = useIsActive() 152 | const provider = useProvider(); 153 | const [res, setRes] = React.useState(null) 154 | const [status, setStatus] = React.useState("loading") 155 | const [timeStamp, setTimeStamp] = React.useState("unknown"); 156 | React.useEffect(() => { 157 | callIpfs(props.cid, setRes, setStatus) 158 | }, [setRes]) 159 | 160 | if (res && status == "loaded") { 161 | const info = extractProofDetails(res, provider, isActive, setTimeStamp); 162 | 163 | return ( 164 | 170 | ) 171 | } else if (status == "loading") { 172 | return ( 173 |
174 | Loading proof 175 |
176 | ) 177 | } else if (status == "failed") { 178 | return ( 179 |
180 | No proof found at cid:{props.cid} 181 |
182 | ) 183 | } 184 | } 185 | 186 | function Badge(props: PublicInput) { 187 | const isActive = useIsActive() 188 | const [verified, setVerified] = React.useState("unknown") 189 | return ( 190 | 191 | 196 | 197 |
198 | 199 | 200 | 201 | {getTokenName("0x"+props.info.account_address.slice(-40))} 202 | 203 | 204 |
205 | 212 |
213 | 214 |
215 | 228 |
229 |
230 |
231 | ) 232 | } 233 | 234 | function DenseTable(props: RowInput) { 235 | return ( 236 | 237 | 238 | 239 | 240 | 241 | 242 | Creator: 243 | 244 | 245 | 246 | 247 | {props.publicEthereumAddress.substr(0, 16)}... 248 | 249 | 250 | 251 | 252 | 253 | 254 | Program hash: 255 | 256 | 257 | 258 | 259 | {props.programHash.substr(0, 16)}... 260 | 261 | 262 | 263 | 264 | 265 | 266 | Security level: 267 | 268 | 269 | 270 | {props.securityLevel} bits 271 | 272 | 273 | 274 | 275 | 276 | Proof age: 277 | 278 | 279 | 280 | 281 | {props.timeStamp == "unknown" ? "Unknown" : moment.unix(props.timeStamp).fromNow()} 282 | 283 | 284 | 285 | 286 | 287 | 288 | Token balance: 289 | 290 | 291 | 292 | {parseInt(props.minTokenBalance)/1e18} 293 | 294 | 295 | 296 |
297 |
298 | ); 299 | } 300 | 301 | function getVerifyButtonText(verified, isActive) { 302 | if (!isActive && verified == "unknown") { 303 | return "Connect wallet to Verify" 304 | } 305 | if (verified == "unknown") { 306 | return "Verify" 307 | } else if (verified == "failed") { 308 | return "Failed" 309 | } else if (verified == "success") { 310 | return "Verified" 311 | } else { 312 | return "Failed" 313 | } 314 | } 315 | 316 | function getVerifyButtonVariant(verified) { 317 | if (verified == "unknown") { 318 | return "text" 319 | } else if (verified == "failed") { 320 | return "outlined" 321 | } else if (verified == "success") { 322 | return "outlined" 323 | } else { 324 | return "outlined" 325 | } 326 | } 327 | 328 | function getVerifyButtonColor(verified) { 329 | if (verified == "unknown") { 330 | return "info" 331 | } else if (verified == "failed") { 332 | return "error" 333 | } else if (verified == "success") { 334 | return "secondary" 335 | } else { 336 | return "error" 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /src/components/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import EthereumWallet from "../Wallet/Ethereum"; 3 | 4 | import AppBar from '@mui/material/AppBar'; 5 | import Box from '@mui/material/Box'; 6 | import Toolbar from '@mui/material/Toolbar'; 7 | import Typography from '@mui/material/Typography'; 8 | import Button from '@mui/material/Button'; 9 | import IconButton from '@mui/material/IconButton'; 10 | import MenuIcon from '@mui/icons-material/Menu'; 11 | import TextField from '@mui/material/TextField'; 12 | import Dialog from '@mui/material/Dialog'; 13 | import DialogActions from '@mui/material/DialogActions'; 14 | import DialogContent from '@mui/material/DialogContent'; 15 | import DialogContentText from '@mui/material/DialogContentText'; 16 | import DialogTitle from '@mui/material/DialogTitle'; 17 | 18 | export default function Header() { 19 | const [openCreate, setOpenCreate] = React.useState(false); 20 | const [openFAQ, setOpenFAQ] = React.useState(false); 21 | 22 | const handleClickOpenCreate = () => { 23 | setOpenCreate(true); 24 | }; 25 | 26 | const handleClickOpenFAQ = () => { 27 | setOpenFAQ(true); 28 | }; 29 | 30 | const handleCloseCreate = () => { 31 | setOpenCreate(false); 32 | }; 33 | 34 | const handleCloseFAQ = () => { 35 | setOpenFAQ(false); 36 | }; 37 | 38 | return ( 39 | 40 | 41 | 42 | 43 | STARK Attestations 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Coming soon 53 | 54 | 55 | To be notified when in-browser proof generation is available please subscribe below. 56 | 57 | 58 | 59 | 60 | 66 | 67 | 68 | 69 | Frequently Asked Questions 70 | 71 | 72 |

What is this?

73 |

STARK attestations are proofs that the creator held a minimum balance of a token at the end of a given block. Importantly, these tokens may be stored on an anonymous account, but the creator must have the ability to sign messages with that account in order to generate the proof.

74 |

Proofs are downloaded from IPFS and verified in browser using the Winterfell codebase.

75 |
76 | 77 |

How do I create an attestation?

78 |

At the moment, attestations can only be generated through the command line. See the Github repo here for instructions. Proof generation takes around five minutes with a peak RAM consumption of around 12GB on a Macbook M1

79 |
80 | 81 |

Are attestations truly private?

82 |

The current proofs are not perfect zero-knowledge, and it is possible that information about the address that tokens are held or the exact token balance could be extracted under certain conditions. Correcting this does not meaningfully change proof generation time, and will be added shortly.

83 |
84 | 85 |

Is it possible to fake a proof?

86 |

Yes! A couple of AIR constraints are not implemented that could be exploited to generate an incorrect proof (although it would be far from trivial). These constraints will be added in shortly.

87 |
88 | 89 |

Are proofs trustless?

90 |

Yes, the only trust assumption is that the blockhash returned from the Ethereum provider belongs to the claimed block. If you're using your own node, then there is nothing to trust.

91 |
92 | 93 |

Can this be used to anonymously prove membership in a DAO, or an NFT collection?

94 |

Yes, the Cairo program can be easily modified to prove arbitrary conditions for any storage value.

95 |
96 | 97 |

How does it work?

98 |

You can read through the balance checker Cairo program here for information (more documentation to come). To confirm that the proof you are viewing corresponds to this particular program, you should check the computed program hash. The correct program hash is 0xcf599536126a617cfcb21610df98c8d08ca668a668b55d3fa9321b89d4504dca (but don't take my word for it -- you can download the code and check for yourself).

99 |
100 | 101 |

What are STARKs?

102 |

A STARK is a non-interactive proof, and stands for "Scalable Transparent Arguments of Knowledge." You can find more resources about them here

103 |
104 | 105 |

What is Cairo?

106 |

Cairo is a programming language written for the Cairo VM. Please see the Cairo website here for more information

107 |
108 | 109 |

When will this be on chain?

110 |

Soon

111 |
112 |
113 |
114 |
115 | ); 116 | } 117 | -------------------------------------------------------------------------------- /src/components/Table/Table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Table from '@mui/material/Table'; 3 | import TableBody from '@mui/material/TableBody'; 4 | import TableCell from '@mui/material/TableCell'; 5 | import TableContainer from '@mui/material/TableContainer'; 6 | import TableHead from '@mui/material/TableHead'; 7 | import TableRow from '@mui/material/TableRow'; 8 | import Paper from '@mui/material/Paper'; 9 | 10 | import { 11 | BrowserRouter as Router, 12 | Link, 13 | } from "react-router-dom"; 14 | 15 | function createData( 16 | account: string, 17 | dao: number, 18 | token: number, 19 | nft: number, 20 | other: number, 21 | ) { 22 | return { account, dao, token, nft, other }; 23 | } 24 | 25 | const rows = [ 26 | createData('0x0000000000000...', 0, 0, 0, 0), 27 | createData('twitter: maxgillett', 1, 1, 1, 0), 28 | ]; 29 | 30 | export default function DenseTable() { 31 | return ( 32 | 33 | 34 | 35 | 36 | Account 37 | DAO 38 | Token 39 | NFT 40 | Other 41 | 42 | 43 | 44 | {rows.map((row) => ( 45 | 50 | 51 | {row.account} 52 | 53 | {row.dao} 54 | {row.token} 55 | {row.nft} 56 | {row.other} 57 | 58 | ))} 59 | 60 |
61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/components/Wallet/Ethereum.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import type { Web3ReactHooks } from '@web3-react/core' 3 | import type { MetaMask } from '@web3-react/metamask' 4 | import { Network } from '@web3-react/network' 5 | import { useState } from 'react' 6 | import { hooks, metaMask } from '../../connectors/metaMask' 7 | import Button from '@mui/material/Button'; 8 | 9 | const { useChainId, useAccounts, useError, useIsActivating, useIsActive, useProvider, useENSNames } = hooks 10 | 11 | export function Connect({ 12 | connector, 13 | chainId, 14 | isActivating, 15 | error, 16 | isActive, 17 | }: { 18 | connector: MetaMask | Network 19 | chainId: ReturnType 20 | isActivating: ReturnType 21 | error: ReturnType 22 | isActive: ReturnType 23 | }) { 24 | const isNetwork = connector instanceof Network 25 | 26 | const [desiredChainId, setDesiredChainId] = useState(isNetwork ? 1 : -1) 27 | 28 | if (error) { 29 | return ( 30 | 40 | ) 41 | } else if (isActive) { 42 | return ( 43 | 46 | ) 47 | } else { 48 | return ( 49 | 64 | ) 65 | } 66 | } 67 | 68 | export default function EthereumWallet(): JSX.Element { 69 | const chainId = useChainId() 70 | const error = useError() 71 | const isActivating = useIsActivating() 72 | const isActive = useIsActive() 73 | 74 | return ( 75 | 82 | ) 83 | } 84 | -------------------------------------------------------------------------------- /src/connectors/metaMask.ts: -------------------------------------------------------------------------------- 1 | import { initializeConnector } from '@web3-react/core' 2 | import { MetaMask } from '@web3-react/metamask' 3 | 4 | export const [metaMask, hooks] = initializeConnector((actions) => new MetaMask(actions)) 5 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import CssBaseline from '@mui/material/CssBaseline'; 4 | import { ThemeProvider } from '@mui/material/styles'; 5 | import App from './App'; 6 | import theme from './theme'; 7 | 8 | const rootElement = document.getElementById('root'); 9 | const root = createRoot(rootElement!); 10 | 11 | root.render( 12 | 13 | {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */} 14 | 15 | 16 | , 17 | ); 18 | 19 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use air::{ProcessorAir, PublicInputs}; 2 | use serde::{Deserialize, Serialize}; 3 | use sha3::{Digest, Sha3_256}; 4 | use winter_math::StarkField; 5 | use winter_utils::{Deserializable, SliceReader}; 6 | use winterfell::StarkProof; 7 | 8 | use js_sys::Uint8Array; 9 | use wasm_bindgen::prelude::*; 10 | 11 | #[derive(Serialize, Deserialize)] 12 | struct ProofData { 13 | input_bytes: Vec, 14 | proof_bytes: Vec, 15 | } 16 | 17 | #[wasm_bindgen] 18 | pub struct ProofInfo { 19 | block_number: u64, 20 | account_address: String, 21 | public_eth_address: String, 22 | token_balance_min: String, 23 | state_root: String, 24 | storage_root: String, 25 | program_hash: String, 26 | security_level: u32, 27 | } 28 | 29 | #[wasm_bindgen] 30 | extern "C" { 31 | #[wasm_bindgen(js_namespace = console)] 32 | fn log(s: &str); 33 | } 34 | 35 | #[wasm_bindgen] 36 | pub fn verify(buffer: &Uint8Array) -> bool { 37 | // Load proof and public input data 38 | let b = buffer.to_vec(); 39 | let data: ProofData = bincode::deserialize(&b).unwrap(); 40 | let pub_inputs = PublicInputs::read_from(&mut SliceReader::new(&data.input_bytes[..])).unwrap(); 41 | let proof = StarkProof::from_bytes(&data.proof_bytes).unwrap(); 42 | 43 | // Verify execution 44 | match winterfell::verify::(proof, pub_inputs) { 45 | Ok(_) => { 46 | log("Execution verified"); 47 | true 48 | } 49 | Err(err) => { 50 | log(format!("Failed to verify execution: {}", err).as_str()); 51 | false 52 | } 53 | } 54 | } 55 | 56 | // TODO: Extract information contained in public input 57 | #[wasm_bindgen] 58 | pub fn get_info(buffer: &Uint8Array) -> ProofInfo { 59 | // Load public input data 60 | let b = buffer.to_vec(); 61 | let data: ProofData = bincode::deserialize(&b).unwrap(); 62 | let pub_inputs = PublicInputs::read_from(&mut SliceReader::new(&data.input_bytes[..])).unwrap(); 63 | let mem = pub_inputs.mem; 64 | 65 | // Print everything to console 66 | //for n in 0..mem.1.len() { 67 | // log(format!("{} {}", mem.0[n], mem.1[n].unwrap().word()).as_str()); 68 | //} 69 | 70 | // Compute program hash 71 | let mut hasher = Sha3_256::new(); 72 | let mut bytes: Vec = vec![]; 73 | for i in 0..7073 { 74 | let felt = mem.1[i].unwrap().word(); 75 | bytes.extend(felt.as_int().to_le_bytes()); 76 | } 77 | hasher.update(&bytes); 78 | let digest = hasher.finalize(); 79 | let program_hash = hex::encode(digest); 80 | 81 | // Extract claimed block, addresses, and minimum balance 82 | let block_number: u64 = mem.1[7073].unwrap().word().as_int().try_into().unwrap(); 83 | let account_address = format!("{}", mem.1[7074].unwrap().word()); 84 | let public_eth_address = format!("{}", mem.1[7075].unwrap().word()); 85 | let token_balance_min = format!("{}", mem.1[7076].unwrap().word()); 86 | 87 | // Extract claimed Merkle roots 88 | let state_root = felts_to_hex(&(7077..7081).map(|i| mem.1[i]).collect::>()); 89 | let storage_root = felts_to_hex(&(7081..7085).map(|i| mem.1[i]).collect::>()); 90 | 91 | // Compute security level 92 | let proof = StarkProof::from_bytes(&data.proof_bytes).unwrap(); 93 | let security_level = proof.security_level(true); 94 | 95 | return ProofInfo { 96 | block_number, 97 | account_address, 98 | public_eth_address, 99 | token_balance_min, 100 | state_root, 101 | storage_root, 102 | program_hash, 103 | security_level, 104 | }; 105 | } 106 | 107 | fn felts_to_hex(arr: &[Option]) -> String { 108 | let mut s = String::new(); 109 | for v in arr { 110 | s.push_str( 111 | format!("{}", v.unwrap().word()) 112 | .chars() 113 | .rev() 114 | .take(16) 115 | .into_iter() 116 | .collect::>() 117 | .iter() 118 | .rev() 119 | .collect::() 120 | .as_str(), 121 | ); 122 | } 123 | s 124 | } 125 | 126 | #[wasm_bindgen] 127 | impl ProofInfo { 128 | #[wasm_bindgen(getter)] 129 | pub fn block_number(&self) -> u64 { 130 | self.block_number 131 | } 132 | #[wasm_bindgen(getter)] 133 | pub fn account_address(&self) -> String { 134 | self.account_address.clone() 135 | } 136 | #[wasm_bindgen(getter)] 137 | pub fn public_eth_address(&self) -> String { 138 | self.public_eth_address.clone() 139 | } 140 | #[wasm_bindgen(getter)] 141 | pub fn token_balance_min(&self) -> String { 142 | self.token_balance_min.clone() 143 | } 144 | #[wasm_bindgen(getter)] 145 | pub fn state_root(&self) -> String { 146 | self.state_root.clone() 147 | } 148 | #[wasm_bindgen(getter)] 149 | pub fn storage_root(&self) -> String { 150 | self.storage_root.clone() 151 | } 152 | #[wasm_bindgen(getter)] 153 | pub fn program_hash(&self) -> String { 154 | self.program_hash.clone() 155 | } 156 | #[wasm_bindgen(getter)] 157 | pub fn security_level(&self) -> u32 { 158 | self.security_level 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/pages/Badges/ViewAccountPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Badge from "../../components/Badge/Badge"; 3 | import { useParams } from 'react-router-dom'; 4 | 5 | function AccountID() { 6 | let params = useParams(); 7 | return params.account 8 | } 9 | 10 | const ViewAccountPage = () => { 11 | return ( 12 |
13 |
14 | Viewing badges for account { AccountID() } 15 |
16 |
17 | 25 |
26 |
27 | ) 28 | } 29 | 30 | export default ViewAccountPage; 31 | -------------------------------------------------------------------------------- /src/pages/Badges/ViewBadgePage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import BadgeViewer from "../../components/Badge/Badge"; 3 | import { useParams } from 'react-router-dom'; 4 | 5 | function BadgeID() { 6 | let params = useParams(); 7 | return params.cid 8 | } 9 | 10 | const ViewBadgePage = () => { 11 | const id = BadgeID(); 12 | return ( 13 |
14 | 15 |
16 | ) 17 | } 18 | 19 | export default ViewBadgePage; 20 | -------------------------------------------------------------------------------- /src/pages/Home/HomePage.tsx: -------------------------------------------------------------------------------- 1 | import Table from "../../components/Table/Table"; 2 | import React from "react"; 3 | 4 | const HomePage = () => { 5 | return ( 6 |
7 | ) 8 | //return ( 9 | //
10 | // 11 | // 12 | //) 13 | } 14 | 15 | export default HomePage; 16 | -------------------------------------------------------------------------------- /src/pages/Mint/MintPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | const ViewBadgePage = () => { 4 | return ( 5 |
6 | Minting badges is not yet supported. 7 |
8 | ) 9 | } 10 | 11 | export default ViewBadgePage; 12 | -------------------------------------------------------------------------------- /src/theme.tsx: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@mui/material/styles'; 2 | import { red } from '@mui/material/colors'; 3 | 4 | // A custom theme for this app 5 | const theme = createTheme({ 6 | palette: { 7 | primary: { 8 | main: '#556cd6', 9 | }, 10 | secondary: { 11 | main: '#19857b', 12 | }, 13 | error: { 14 | main: red.A400, 15 | }, 16 | }, 17 | }); 18 | 19 | export default theme; 20 | 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ts-node": { 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | }, 7 | "compilerOptions": { 8 | "target": "es5", 9 | "lib": ["dom", "dom.iterable", "esnext"], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react", 22 | "noImplicitAny": false 23 | }, 24 | "include": ["src"] 25 | } 26 | 27 | -------------------------------------------------------------------------------- /webpack.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { Configuration, ProvidePlugin } from "webpack"; 3 | import * as webpackDevServer from "webpack-dev-server"; 4 | import WasmPackPlugin from "@wasm-tool/wasm-pack-plugin"; 5 | import HtmlWebpackPlugin from "html-webpack-plugin"; 6 | import NodePolyfillPlugin from "node-polyfill-webpack-plugin"; 7 | 8 | const config: Configuration = { 9 | entry: "./src/index.tsx", 10 | output: { 11 | path: path.resolve(__dirname, "build"), 12 | filename: "bundle.js", 13 | publicPath: '/public/', 14 | }, 15 | plugins: [ 16 | new WasmPackPlugin({ 17 | crateDirectory: path.resolve(__dirname, ".") 18 | }), 19 | new NodePolyfillPlugin() 20 | ], 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.(ts|js)x?$/, 25 | exclude: /node_modules/, 26 | use: { 27 | loader: "babel-loader", 28 | options: { 29 | presets: [ 30 | "@babel/preset-env", 31 | "@babel/preset-react", 32 | "@babel/preset-typescript", 33 | ], 34 | }, 35 | }, 36 | }, 37 | { 38 | test: /\.bin/, 39 | type: 'asset/inline', 40 | generator: { 41 | dataUrl: content => { 42 | return content; 43 | } 44 | } 45 | } 46 | ], 47 | }, 48 | resolve: { 49 | extensions: [".tsx", ".ts", ".js"], 50 | }, 51 | experiments: { 52 | asyncWebAssembly: true, 53 | }, 54 | mode: "production", 55 | performance: { 56 | maxEntrypointSize: 8000000, 57 | maxAssetSize: 8000000, 58 | }, 59 | optimization: { 60 | minimize: false 61 | }, 62 | devServer: { 63 | host: "0.0.0.0", 64 | port: 3000, 65 | historyApiFallback: true, 66 | headers: { 67 | "Access-Control-Allow-Origin": "*", 68 | } 69 | } 70 | }; 71 | 72 | export default config; 73 | --------------------------------------------------------------------------------