├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src ├── data_structures.rs ├── error.rs ├── main.rs ├── prover.rs ├── rng.rs └── verifier.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "ark-bls12-381" 18 | version = "0.3.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "65be532f9dd1e98ad0150b037276cde464c6f371059e6dd02c0222395761f6aa" 21 | dependencies = [ 22 | "ark-ec", 23 | "ark-ff", 24 | "ark-std", 25 | ] 26 | 27 | [[package]] 28 | name = "ark-ec" 29 | version = "0.3.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "dea978406c4b1ca13c2db2373b05cc55429c3575b8b21f1b9ee859aa5b03dd42" 32 | dependencies = [ 33 | "ark-ff", 34 | "ark-serialize", 35 | "ark-std", 36 | "derivative", 37 | "num-traits", 38 | "zeroize", 39 | ] 40 | 41 | [[package]] 42 | name = "ark-ff" 43 | version = "0.3.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" 46 | dependencies = [ 47 | "ark-ff-asm", 48 | "ark-ff-macros", 49 | "ark-serialize", 50 | "ark-std", 51 | "derivative", 52 | "num-bigint", 53 | "num-traits", 54 | "paste", 55 | "rustc_version", 56 | "zeroize", 57 | ] 58 | 59 | [[package]] 60 | name = "ark-ff-asm" 61 | version = "0.3.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" 64 | dependencies = [ 65 | "quote", 66 | "syn", 67 | ] 68 | 69 | [[package]] 70 | name = "ark-ff-macros" 71 | version = "0.3.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" 74 | dependencies = [ 75 | "num-bigint", 76 | "num-traits", 77 | "quote", 78 | "syn", 79 | ] 80 | 81 | [[package]] 82 | name = "ark-poly" 83 | version = "0.3.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "7b0f78f47537c2f15706db7e98fe64cc1711dbf9def81218194e17239e53e5aa" 86 | dependencies = [ 87 | "ark-ff", 88 | "ark-serialize", 89 | "ark-std", 90 | "derivative", 91 | "hashbrown", 92 | ] 93 | 94 | [[package]] 95 | name = "ark-poly-commit" 96 | version = "0.3.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "a71ddfa72bad1446cab7bbecb6018dbbdc9abcbc3a0065483ae5186ad2a64dcd" 99 | dependencies = [ 100 | "ark-ec", 101 | "ark-ff", 102 | "ark-poly", 103 | "ark-serialize", 104 | "ark-std", 105 | "derivative", 106 | "digest", 107 | "tracing", 108 | ] 109 | 110 | [[package]] 111 | name = "ark-serialize" 112 | version = "0.3.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" 115 | dependencies = [ 116 | "ark-serialize-derive", 117 | "ark-std", 118 | "digest", 119 | ] 120 | 121 | [[package]] 122 | name = "ark-serialize-derive" 123 | version = "0.3.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "8dd4e5f0bf8285d5ed538d27fab7411f3e297908fd93c62195de8bee3f199e82" 126 | dependencies = [ 127 | "proc-macro2", 128 | "quote", 129 | "syn", 130 | ] 131 | 132 | [[package]] 133 | name = "ark-std" 134 | version = "0.3.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" 137 | dependencies = [ 138 | "num-traits", 139 | "rand", 140 | ] 141 | 142 | [[package]] 143 | name = "autocfg" 144 | version = "1.1.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 147 | 148 | [[package]] 149 | name = "blake2" 150 | version = "0.9.2" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" 153 | dependencies = [ 154 | "crypto-mac", 155 | "digest", 156 | "opaque-debug", 157 | ] 158 | 159 | [[package]] 160 | name = "cfg-if" 161 | version = "1.0.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 164 | 165 | [[package]] 166 | name = "crypto-mac" 167 | version = "0.8.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" 170 | dependencies = [ 171 | "generic-array", 172 | "subtle", 173 | ] 174 | 175 | [[package]] 176 | name = "derivative" 177 | version = "2.2.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 180 | dependencies = [ 181 | "proc-macro2", 182 | "quote", 183 | "syn", 184 | ] 185 | 186 | [[package]] 187 | name = "digest" 188 | version = "0.9.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 191 | dependencies = [ 192 | "generic-array", 193 | ] 194 | 195 | [[package]] 196 | name = "generic-array" 197 | version = "0.14.6" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 200 | dependencies = [ 201 | "typenum", 202 | "version_check", 203 | ] 204 | 205 | [[package]] 206 | name = "getrandom" 207 | version = "0.2.8" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 210 | dependencies = [ 211 | "cfg-if", 212 | "libc", 213 | "wasi", 214 | ] 215 | 216 | [[package]] 217 | name = "hashbrown" 218 | version = "0.11.2" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 221 | dependencies = [ 222 | "ahash", 223 | ] 224 | 225 | [[package]] 226 | name = "libc" 227 | version = "0.2.137" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 230 | 231 | [[package]] 232 | name = "num-bigint" 233 | version = "0.4.3" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 236 | dependencies = [ 237 | "autocfg", 238 | "num-integer", 239 | "num-traits", 240 | ] 241 | 242 | [[package]] 243 | name = "num-integer" 244 | version = "0.1.45" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 247 | dependencies = [ 248 | "autocfg", 249 | "num-traits", 250 | ] 251 | 252 | [[package]] 253 | name = "num-traits" 254 | version = "0.2.15" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 257 | dependencies = [ 258 | "autocfg", 259 | ] 260 | 261 | [[package]] 262 | name = "once_cell" 263 | version = "1.16.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 266 | 267 | [[package]] 268 | name = "opaque-debug" 269 | version = "0.3.0" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 272 | 273 | [[package]] 274 | name = "paste" 275 | version = "1.0.9" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" 278 | 279 | [[package]] 280 | name = "pest" 281 | version = "2.4.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" 284 | dependencies = [ 285 | "thiserror", 286 | "ucd-trie", 287 | ] 288 | 289 | [[package]] 290 | name = "pin-project-lite" 291 | version = "0.2.9" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 294 | 295 | [[package]] 296 | name = "ppv-lite86" 297 | version = "0.2.16" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 300 | 301 | [[package]] 302 | name = "proc-macro2" 303 | version = "1.0.47" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 306 | dependencies = [ 307 | "unicode-ident", 308 | ] 309 | 310 | [[package]] 311 | name = "prompt" 312 | version = "0.1.0" 313 | source = "git+https://github.com/kobigurk/zkhack-prompt#e7a1afa053034caa485184f3f53044463680b0e2" 314 | 315 | [[package]] 316 | name = "quote" 317 | version = "1.0.21" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 320 | dependencies = [ 321 | "proc-macro2", 322 | ] 323 | 324 | [[package]] 325 | name = "rand" 326 | version = "0.8.5" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 329 | dependencies = [ 330 | "rand_chacha", 331 | "rand_core", 332 | ] 333 | 334 | [[package]] 335 | name = "rand_chacha" 336 | version = "0.3.1" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 339 | dependencies = [ 340 | "ppv-lite86", 341 | "rand_core", 342 | ] 343 | 344 | [[package]] 345 | name = "rand_core" 346 | version = "0.6.4" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 349 | 350 | [[package]] 351 | name = "rustc_version" 352 | version = "0.3.3" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" 355 | dependencies = [ 356 | "semver", 357 | ] 358 | 359 | [[package]] 360 | name = "semver" 361 | version = "0.11.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 364 | dependencies = [ 365 | "semver-parser", 366 | ] 367 | 368 | [[package]] 369 | name = "semver-parser" 370 | version = "0.10.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 373 | dependencies = [ 374 | "pest", 375 | ] 376 | 377 | [[package]] 378 | name = "subtle" 379 | version = "2.4.1" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 382 | 383 | [[package]] 384 | name = "sumcheck-puzzle" 385 | version = "0.1.0" 386 | dependencies = [ 387 | "ark-bls12-381", 388 | "ark-ff", 389 | "ark-poly", 390 | "ark-poly-commit", 391 | "ark-std", 392 | "blake2", 393 | "digest", 394 | "prompt", 395 | "rand_chacha", 396 | ] 397 | 398 | [[package]] 399 | name = "syn" 400 | version = "1.0.103" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 403 | dependencies = [ 404 | "proc-macro2", 405 | "quote", 406 | "unicode-ident", 407 | ] 408 | 409 | [[package]] 410 | name = "synstructure" 411 | version = "0.12.6" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 414 | dependencies = [ 415 | "proc-macro2", 416 | "quote", 417 | "syn", 418 | "unicode-xid", 419 | ] 420 | 421 | [[package]] 422 | name = "thiserror" 423 | version = "1.0.37" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 426 | dependencies = [ 427 | "thiserror-impl", 428 | ] 429 | 430 | [[package]] 431 | name = "thiserror-impl" 432 | version = "1.0.37" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 435 | dependencies = [ 436 | "proc-macro2", 437 | "quote", 438 | "syn", 439 | ] 440 | 441 | [[package]] 442 | name = "tracing" 443 | version = "0.1.37" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 446 | dependencies = [ 447 | "cfg-if", 448 | "pin-project-lite", 449 | "tracing-attributes", 450 | "tracing-core", 451 | ] 452 | 453 | [[package]] 454 | name = "tracing-attributes" 455 | version = "0.1.23" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 458 | dependencies = [ 459 | "proc-macro2", 460 | "quote", 461 | "syn", 462 | ] 463 | 464 | [[package]] 465 | name = "tracing-core" 466 | version = "0.1.30" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 469 | 470 | [[package]] 471 | name = "typenum" 472 | version = "1.15.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 475 | 476 | [[package]] 477 | name = "ucd-trie" 478 | version = "0.1.5" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" 481 | 482 | [[package]] 483 | name = "unicode-ident" 484 | version = "1.0.5" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 487 | 488 | [[package]] 489 | name = "unicode-xid" 490 | version = "0.2.4" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 493 | 494 | [[package]] 495 | name = "version_check" 496 | version = "0.9.4" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 499 | 500 | [[package]] 501 | name = "wasi" 502 | version = "0.11.0+wasi-snapshot-preview1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 505 | 506 | [[package]] 507 | name = "zeroize" 508 | version = "1.5.7" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" 511 | dependencies = [ 512 | "zeroize_derive", 513 | ] 514 | 515 | [[package]] 516 | name = "zeroize_derive" 517 | version = "1.3.2" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" 520 | dependencies = [ 521 | "proc-macro2", 522 | "quote", 523 | "syn", 524 | "synstructure", 525 | ] 526 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sumcheck-puzzle" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ark-ff = { version = "^0.3.0", default-features = false } 10 | ark-std = { version = "^0.3.0", default-features = false } 11 | ark-poly = { version = "^0.3.0", default-features = false } 12 | ark-poly-commit = { version = "^0.3.0", default-features = false } 13 | 14 | digest = { version = "0.9" } 15 | ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "curve" ] } 16 | blake2 = { version = "0.9", default-features = false } 17 | rand_chacha = { version = "0.3.0", default-features = false } 18 | 19 | prompt = { git = "https://github.com/kobigurk/zkhack-prompt" } 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # puzzle-zero-sum-game 2 | ---------------------- 3 | 4 | **DO NOT FORK THE REPOSITORY, AS IT WILL MAKE YOUR SOLUTION PUBLIC. INSTEAD, CLONE IT AND ADD A NEW REMOTE TO A PRIVATE REPOSITORY, OR SUBMIT A GIST** 5 | 6 | Trying it out 7 | ============= 8 | 9 | Use `cargo run --release` to see it in action 10 | 11 | Submitting a solution 12 | ===================== 13 | 14 | [Submit a solution](https://xng1lsio92y.typeform.com/to/OTZePUDE) 15 | 16 | [Submit a write-up](https://xng1lsio92y.typeform.com/to/Q1IC7Tok) 17 | 18 | Puzzle description 19 | ================== 20 | 21 | ``` 22 | ______ _ __ _ _ _ 23 | |___ /| | / / | | | | | | 24 | / / | |/ / | |_| | __ _ ___| | __ 25 | / / | \ | _ |/ _` |/ __| |/ / 26 | ./ /___| |\ \ | | | | (_| | (__| < 27 | \_____/\_| \_/ \_| |_/\__,_|\___|_|\_\ 28 | 29 | Bob has designed a new private payments protocol design, where every note comes with a secret 30 | polynomial f whose sum over a specific set is zero. This is enforced using a sumcheck protocol. 31 | Once a note is spent, f is modified to a different polynomial whose sum isn't zero. One day, 32 | after an interesting conversation with her friends, Alice got an idea for an attack that can 33 | potentially allow her to double spend notes. 34 | 35 | Alice successfully double spent a note. Can you figure out how she did it? 36 | 37 | Be very careful, if the verifier somehow learns the sum of the modified f, 38 | they can deanonymize you. 39 | 40 | In the rest of protocol that is not described here, the masking polynomial used by 41 | the prover is opened twice. Therefore, the masking polynomial cannot be a 42 | constant polynomial. 43 | 44 | To see examples of sumcheck, you can review the protocol described in 45 | https://github.com/arkworks-rs/marlin/blob/master/diagram/diagram.pdf. 46 | ``` 47 | -------------------------------------------------------------------------------- /src/data_structures.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FftField, Field, ToBytes}; 2 | use ark_poly::{univariate::DensePolynomial, GeneralEvaluationDomain}; 3 | use ark_poly_commit::PolynomialCommitment; 4 | 5 | pub struct Statement>> { 6 | pub domain: GeneralEvaluationDomain, 7 | pub f: PC::Commitment, 8 | pub sum: F, 9 | } 10 | 11 | impl>> ToBytes for Statement { 12 | fn write(&self, mut writer: W) -> ark_std::io::Result<()> { 13 | self.f.write(&mut writer)?; 14 | self.sum.write(&mut writer) 15 | } 16 | } 17 | 18 | pub struct Proof>> { 19 | pub f_opening: F, 20 | pub s: PC::Commitment, 21 | pub s_opening: F, 22 | pub g: PC::Commitment, 23 | pub g_opening: F, 24 | pub h: PC::Commitment, 25 | pub h_opening: F, 26 | pub pc_proof: PC::BatchProof, 27 | } 28 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum Error { 3 | OpeningError, 4 | PolynomialCommitmentError(E), 5 | IncorrectSum, 6 | } 7 | 8 | impl Error { 9 | pub fn from_pc_err(err: E) -> Self { 10 | Error::PolynomialCommitmentError(err) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | pub mod data_structures; 2 | pub mod error; 3 | pub mod prover; 4 | pub mod rng; 5 | pub mod verifier; 6 | 7 | use ark_bls12_381::{Bls12_381, Fr as F}; 8 | use ark_ff::Zero; 9 | use ark_poly::{ 10 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, 11 | UVPolynomial, 12 | }; 13 | use ark_poly_commit::{marlin_pc::MarlinKZG10, LabeledPolynomial, PolynomialCommitment}; 14 | use ark_std::{rand::rngs::StdRng, test_rng}; 15 | use blake2::Blake2s; 16 | use prompt::{puzzle, welcome}; 17 | use rand_chacha::ChaChaRng; 18 | use rng::SimpleHashFiatShamirRng; 19 | 20 | use crate::{data_structures::Statement, prover::prove, verifier::verify}; 21 | 22 | pub const PROTOCOL_NAME: &'static [u8] = b"GEOMETRY-SUMCHECK"; 23 | pub type PC = MarlinKZG10>; 24 | type FS = SimpleHashFiatShamirRng; 25 | 26 | fn main() { 27 | welcome(); 28 | puzzle(PUZZLE_DESCRIPTION); 29 | 30 | let domain_size = 16; 31 | let domain = GeneralEvaluationDomain::new(domain_size).unwrap(); 32 | let max_degree = 30; 33 | 34 | let mut rng = test_rng(); 35 | let srs = PC::setup(max_degree, None, &mut rng).unwrap(); 36 | 37 | let (ck, vk) = PC::trim(&srs, max_degree, 1, Some(&[domain_size - 2])).unwrap(); 38 | 39 | let coeffs = vec![ 40 | F::from(123312u64), 41 | F::from(124151231u64), 42 | F::from(1190283019u64), 43 | F::from(19312315u64), 44 | F::from(312423151u64), 45 | F::from(61298741u64), 46 | F::from(132151231u64), 47 | F::from(1321512314u64), 48 | F::from(721315123151u64), 49 | F::from(783749123u64), 50 | F::from(2135123151u64), 51 | F::from(312512314u64), 52 | F::from(23194890182314u64), 53 | F::from(321514231512u64), 54 | F::from(321451231512u64), 55 | F::from(823897129831u64), 56 | F::from(908241231u64), 57 | F::from(9837249823u64), 58 | F::from(982398741823u64), 59 | F::from(3891748912u64), 60 | F::from(21389749812u64), 61 | F::from(891724876431u64), 62 | F::from(213145213u64), 63 | F::from(32897498123u64), 64 | F::from(3219851289231u64), 65 | F::from(2184718923u64), 66 | F::from(31245123131431u64), 67 | F::from(36712398759812u64), 68 | F::from(8724876123u64), 69 | F::from(89783927412u64), 70 | F::from(8723498123u64), 71 | ]; 72 | let f = DensePolynomial::from_coefficients_slice(&coeffs); 73 | 74 | // begin sanity 75 | let mut real_sum = F::zero(); 76 | for h in domain.elements() { 77 | real_sum += f.evaluate(&h); 78 | } 79 | assert_ne!(real_sum, F::zero()); 80 | // end sanity 81 | 82 | let sum = F::zero(); 83 | 84 | let f = LabeledPolynomial::new("f".into(), f.clone(), None, Some(1)); 85 | let (f_commitment, f_rand) = PC::commit(&ck, &[f.clone()], Some(&mut rng)).unwrap(); 86 | 87 | let statement = Statement { 88 | domain, 89 | f: f_commitment[0].commitment().clone(), 90 | sum, 91 | }; 92 | 93 | let proof = prove::(&ck, &statement, &f, &f_rand[0], &mut rng).unwrap(); 94 | 95 | let res = verify::(&vk, &statement, &proof, &mut rng); 96 | assert_eq!(true, res.is_ok()); 97 | } 98 | 99 | const PUZZLE_DESCRIPTION: &str = "\ 100 | Bob has designed a new private payments protocol design, where every note comes with a secret 101 | polynomial f whose sum over a specific set is zero. This is enforced using a sumcheck protocol. 102 | Once a note is spent, f is modified to a different polynomial whose sum isn't zero. One day, 103 | after an interesting conversation with her friends, Alice got an idea for an attack that can 104 | potentially allow her to double spend notes. 105 | 106 | Alice successfully double spent a note. Can you figure out how she did it? 107 | 108 | Be very careful, if the verifier somehow learns the sum of the modified f, 109 | they can deanonymize you. 110 | 111 | In the rest of protocol that is not described here, the masking polynomial used by 112 | the prover is opened twice. Therefore, the masking polynomial cannot be a 113 | constant polynomial. 114 | 115 | To see examples of sumcheck, you can review the protocol described in 116 | https://github.com/arkworks-rs/marlin/blob/master/diagram/diagram.pdf. 117 | "; 118 | -------------------------------------------------------------------------------- /src/prover.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::FftField; 2 | use ark_poly::univariate::DensePolynomial; 3 | use ark_poly_commit::{LabeledPolynomial, PolynomialCommitment}; 4 | use ark_std::rand::RngCore; 5 | 6 | use crate::{ 7 | data_structures::{Proof, Statement}, 8 | error::Error, 9 | rng::FiatShamirRng, 10 | }; 11 | 12 | pub fn prove< 13 | F: FftField, 14 | PC: PolynomialCommitment>, 15 | FS: FiatShamirRng, 16 | R: RngCore, 17 | >( 18 | ck: &PC::CommitterKey, 19 | statement: &Statement, 20 | f: &LabeledPolynomial>, 21 | f_rand: &PC::Randomness, 22 | rng: &mut R, 23 | ) -> Result, Error> { 24 | /* 25 | ADD YOUR CODE HERE 26 | */ 27 | /* 28 | In the rest of protocol that is not described here, the masking polynomial is opened twice. Therefore, the masking polynomial cannot be a constant polynomial. 29 | */ 30 | todo!(); 31 | } 32 | -------------------------------------------------------------------------------- /src/rng.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FromBytes, ToBytes}; 2 | use ark_std::convert::From; 3 | use ark_std::marker::PhantomData; 4 | use ark_std::rand::{RngCore, SeedableRng}; 5 | use digest::Digest; 6 | 7 | use ark_std::vec::Vec; 8 | 9 | /// An RNG suitable for Fiat-Shamir transforms 10 | pub trait FiatShamirRng: RngCore { 11 | /// Create a new `Self` with an initial input 12 | fn initialize<'a, T: 'a + ToBytes>(initial_input: &'a T) -> Self; 13 | /// Absorb new inputs into state 14 | fn absorb<'a, T: 'a + ToBytes>(&mut self, new_input: &'a T); 15 | } 16 | 17 | /// A simple `FiatShamirRng` that refreshes its seed by hashing together the previous seed 18 | /// and the new seed material. 19 | pub struct SimpleHashFiatShamirRng { 20 | r: R, 21 | seed: [u8; 32], 22 | #[doc(hidden)] 23 | digest: PhantomData, 24 | } 25 | 26 | impl RngCore for SimpleHashFiatShamirRng { 27 | #[inline] 28 | fn next_u32(&mut self) -> u32 { 29 | self.r.next_u32() 30 | } 31 | 32 | #[inline] 33 | fn next_u64(&mut self) -> u64 { 34 | self.r.next_u64() 35 | } 36 | 37 | #[inline] 38 | fn fill_bytes(&mut self, dest: &mut [u8]) { 39 | self.r.fill_bytes(dest); 40 | } 41 | 42 | #[inline] 43 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ark_std::rand::Error> { 44 | Ok(self.r.fill_bytes(dest)) 45 | } 46 | } 47 | 48 | impl FiatShamirRng for SimpleHashFiatShamirRng 49 | where 50 | R::Seed: From<[u8; 32]>, 51 | { 52 | /// Create a new `Self` by initializing with a fresh seed. 53 | /// `self.seed = H(initial_input)`. 54 | #[inline] 55 | fn initialize<'a, T: 'a + ToBytes>(initial_input: &'a T) -> Self { 56 | let mut bytes = Vec::new(); 57 | initial_input 58 | .write(&mut bytes) 59 | .expect("failed to convert to bytes"); 60 | let seed = FromBytes::read(D::digest(&bytes).as_ref()).expect("failed to get [u8; 32]"); 61 | let r = R::from_seed(::from(seed)); 62 | Self { 63 | r, 64 | seed: seed, 65 | digest: PhantomData, 66 | } 67 | } 68 | 69 | /// Refresh `self.seed` with new material. Achieved by setting 70 | /// `self.seed = H(new_input || self.seed)`. 71 | #[inline] 72 | fn absorb<'a, T: 'a + ToBytes>(&mut self, new_input: &'a T) { 73 | let mut bytes = Vec::new(); 74 | new_input 75 | .write(&mut bytes) 76 | .expect("failed to convert to bytes"); 77 | bytes.extend_from_slice(&self.seed); 78 | self.seed = FromBytes::read(D::digest(&bytes).as_ref()).expect("failed to get [u8; 32]"); 79 | self.r = R::from_seed(::from(self.seed)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/verifier.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{to_bytes, FftField}; 2 | use ark_poly::{univariate::DensePolynomial, EvaluationDomain}; 3 | use ark_poly_commit::{Evaluations, LabeledCommitment, PolynomialCommitment, QuerySet}; 4 | use ark_std::rand::RngCore; 5 | 6 | use crate::{ 7 | data_structures::{Proof, Statement}, 8 | error::Error, 9 | rng::FiatShamirRng, 10 | PROTOCOL_NAME, 11 | }; 12 | 13 | pub fn verify< 14 | F: FftField, 15 | PC: PolynomialCommitment>, 16 | FS: FiatShamirRng, 17 | R: RngCore, 18 | >( 19 | vk: &PC::VerifierKey, 20 | statement: &Statement, 21 | proof: &Proof, 22 | rng: &mut R, 23 | ) -> Result<(), Error> { 24 | let mut fs_rng = FS::initialize(&to_bytes![&PROTOCOL_NAME, statement].unwrap()); 25 | 26 | fs_rng.absorb(&to_bytes![proof.s, proof.h, proof.g].unwrap()); 27 | let f = LabeledCommitment::new("f".into(), statement.f.clone(), None); 28 | let s = LabeledCommitment::new("s".into(), proof.s.clone(), None); 29 | let h = LabeledCommitment::new("h".into(), proof.h.clone(), None); 30 | let g = LabeledCommitment::new( 31 | "g".into(), 32 | proof.g.clone(), 33 | Some(statement.domain.size() - 2), 34 | ); 35 | 36 | let xi = F::rand(&mut fs_rng); 37 | let opening_challenge = F::rand(&mut fs_rng); 38 | 39 | let point_label = String::from("xi"); 40 | let query_set = QuerySet::from([ 41 | ("f".into(), (point_label.clone(), xi)), 42 | ("h".into(), (point_label.clone(), xi)), 43 | ("g".into(), (point_label.clone(), xi)), 44 | ("s".into(), (point_label, xi)), 45 | ]); 46 | 47 | let evaluations = Evaluations::from([ 48 | (("f".into(), xi), proof.f_opening), 49 | (("s".into(), xi), proof.s_opening), 50 | (("h".into(), xi), proof.h_opening), 51 | (("g".into(), xi), proof.g_opening), 52 | ]); 53 | 54 | let res = PC::batch_check( 55 | vk, 56 | &[f, s, h, g], 57 | &query_set, 58 | &evaluations, 59 | &proof.pc_proof, 60 | opening_challenge, 61 | rng, 62 | ) 63 | .map_err(Error::from_pc_err)?; 64 | 65 | if !res { 66 | return Err(Error::OpeningError); 67 | } 68 | 69 | let card_inverse = statement.domain.size_as_field_element().inverse().unwrap(); 70 | let lhs = proof.s_opening + proof.f_opening; 71 | let rhs = { 72 | let x_gx = xi * proof.g_opening; 73 | let zh_eval = statement.domain.evaluate_vanishing_polynomial(xi); 74 | 75 | x_gx + proof.h_opening * zh_eval + statement.sum * card_inverse 76 | }; 77 | 78 | if lhs != rhs { 79 | return Err(Error::IncorrectSum); 80 | } 81 | 82 | Ok(()) 83 | } 84 | --------------------------------------------------------------------------------