diff --git a/Cargo.lock b/Cargo.lock index 1ef2f79..9a2540e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -557,6 +557,17 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2b_simd" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -593,6 +604,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" + [[package]] name = "byteorder" version = "1.5.0" @@ -657,6 +674,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "cpufeatures" version = "0.2.14" @@ -681,6 +704,31 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.2" @@ -918,6 +966,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ + "bitvec", "byteorder", "ff_derive", "rand_core 0.6.4", @@ -1027,6 +1076,34 @@ dependencies = [ "subtle", ] +[[package]] +name = "halo2curves-axiom" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8309e4638b4f1bcf6613d72265a84074d26034c35edc5d605b5688e580b8b8" +dependencies = [ + "blake2b_simd", + "digest 0.10.7", + "ff", + "group", + "hex", + "lazy_static", + "num-bigint 0.4.6", + "num-traits", + "pairing", + "pasta_curves", + "paste", + "rand 0.8.5", + "rand_core 0.6.4", + "rayon", + "serde", + "serde_arrays", + "sha2 0.10.8", + "static_assertions", + "subtle", + "unroll", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -1034,10 +1111,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", + "equivalent", "foldhash", "serde", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1062,6 +1146,12 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hmac" version = "0.12.1" @@ -1129,6 +1219,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1185,6 +1284,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1250,6 +1352,15 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1289,6 +1400,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "rand 0.8.5", ] [[package]] @@ -1320,6 +1432,33 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-prime" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e238432a7881ec7164503ccc516c014bf009be7984cde1ba56837862543bdec3" +dependencies = [ + "bitvec", + "either", + "lru", + "num-bigint 0.4.6", + "num-integer", + "num-modular", + "num-traits", + "rand 0.8.5", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -1372,6 +1511,18 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "nums" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3c74f925fb8cfc49a8022f2afce48a0683b70f9e439885594e84c5edbf5b01" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "rand 0.8.5", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1384,6 +1535,185 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openvm" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "bytemuck", + "num-bigint 0.4.6", + "openvm-custom-insn", + "openvm-platform", + "openvm-rv32im-guest", + "serde", +] + +[[package]] +name = "openvm-algebra-complex-macros" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "openvm-macros-common", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "openvm-algebra-guest" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "halo2curves-axiom", + "num-bigint 0.4.6", + "once_cell", + "openvm-algebra-complex-macros", + "openvm-algebra-moduli-macros", + "openvm-custom-insn", + "openvm-rv32im-guest", + "serde-big-array", + "strum_macros", +] + +[[package]] +name = "openvm-algebra-moduli-macros" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "num-bigint 0.4.6", + "num-prime", + "openvm-macros-common", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "openvm-custom-insn" +version = "0.1.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "openvm-ecc-guest" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "ecdsa", + "elliptic-curve", + "group", + "halo2curves-axiom", + "once_cell", + "openvm", + "openvm-algebra-guest", + "openvm-custom-insn", + "openvm-ecc-sw-macros", + "openvm-rv32im-guest", + "serde", + "strum_macros", +] + +[[package]] +name = "openvm-ecc-sw-macros" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "openvm-macros-common", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "openvm-macros-common" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "syn 2.0.100", +] + +[[package]] +name = "openvm-pairing" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "group", + "hex-literal", + "itertools 0.14.0", + "num-bigint 0.4.6", + "num-traits", + "openvm", + "openvm-algebra-complex-macros", + "openvm-algebra-guest", + "openvm-algebra-moduli-macros", + "openvm-custom-insn", + "openvm-ecc-guest", + "openvm-ecc-sw-macros", + "openvm-pairing-guest", + "openvm-platform", + "openvm-rv32im-guest", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "openvm-pairing-guest" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "hex-literal", + "itertools 0.14.0", + "lazy_static", + "num-bigint 0.4.6", + "num-traits", + "openvm", + "openvm-algebra-guest", + "openvm-algebra-moduli-macros", + "openvm-custom-insn", + "openvm-ecc-guest", + "rand 0.8.5", + "serde", + "strum_macros", +] + +[[package]] +name = "openvm-platform" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "libm", + "openvm-custom-insn", + "openvm-rv32im-guest", +] + +[[package]] +name = "openvm-rv32im-guest" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "openvm-custom-insn", + "p3-field 0.1.0", + "strum_macros", +] + +[[package]] +name = "openvm-sha2" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "openvm-sha256-guest", + "sha2 0.10.8", +] + +[[package]] +name = "openvm-sha256-guest" +version = "1.2.1-rc.0" +source = "git+https://github.com/openvm-org/openvm.git?rev=9ef822c947f14eb436e293971c969cfcb688dbe7#9ef822c947f14eb436e293971c969cfcb688dbe7" +dependencies = [ + "openvm-platform", +] + [[package]] name = "p256" version = "0.13.2" @@ -1403,7 +1733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080896e9d09e9761982febafe3b3da5cbf320e32f0c89b6e2e01e875129f4c2d" dependencies = [ "num-bigint 0.4.6", - "p3-field", + "p3-field 0.2.0-succinct", "p3-mds", "p3-poseidon2", "p3-symmetric", @@ -1417,10 +1747,27 @@ version = "0.2.0-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "292e97d02d4c38d8b306c2b8c0428bf15f4d32a11a40bcf80018f675bf33267e" dependencies = [ - "p3-field", + "p3-field 0.2.0-succinct", "p3-matrix", - "p3-maybe-rayon", - "p3-util", + "p3-maybe-rayon 0.2.0", + "p3-util 0.2.0", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.1.0" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=539bbc84085efb609f4f62cb03cf49588388abdb#539bbc84085efb609f4f62cb03cf49588388abdb" +dependencies = [ + "itertools 0.14.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "nums", + "p3-maybe-rayon 0.1.0", + "p3-util 0.1.0", + "rand 0.8.5", + "serde", "tracing", ] @@ -1433,7 +1780,7 @@ dependencies = [ "itertools 0.12.1", "num-bigint 0.4.6", "num-traits", - "p3-util", + "p3-util 0.2.0", "rand 0.8.5", "serde", ] @@ -1445,14 +1792,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98bf2c7680b8e906a5e147fe4ceb05a11cc9fa35678aa724333bcb35c72483c1" dependencies = [ "itertools 0.12.1", - "p3-field", - "p3-maybe-rayon", - "p3-util", + "p3-field 0.2.0-succinct", + "p3-maybe-rayon 0.2.0", + "p3-util 0.2.0", "rand 0.8.5", "serde", "tracing", ] +[[package]] +name = "p3-maybe-rayon" +version = "0.1.0" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=539bbc84085efb609f4f62cb03cf49588388abdb#539bbc84085efb609f4f62cb03cf49588388abdb" + [[package]] name = "p3-maybe-rayon" version = "0.2.0" @@ -1467,10 +1819,10 @@ checksum = "706cea48976f54702dc68dffa512684c1304d1a3606cadea423cfe0b1ee25134" dependencies = [ "itertools 0.12.1", "p3-dft", - "p3-field", + "p3-field 0.2.0-succinct", "p3-matrix", "p3-symmetric", - "p3-util", + "p3-util 0.2.0", "rand 0.8.5", ] @@ -1481,7 +1833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2ce5f5ec7f1ba3a233a671621029def7bd416e7c51218c9d1167d21602cf312" dependencies = [ "gcd", - "p3-field", + "p3-field 0.2.0-succinct", "p3-mds", "p3-symmetric", "rand 0.8.5", @@ -1495,7 +1847,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f29dc5bb6c99d3de75869d5c086874b64890280eeb7d3e068955f939e219253" dependencies = [ "itertools 0.12.1", - "p3-field", + "p3-field 0.2.0-succinct", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.1.0" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=539bbc84085efb609f4f62cb03cf49588388abdb#539bbc84085efb609f4f62cb03cf49588388abdb" +dependencies = [ "serde", ] @@ -1543,6 +1903,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff", + "group", + "lazy_static", + "rand 0.8.5", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -1784,6 +2159,26 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1941,6 +2336,7 @@ dependencies = [ "ripemd", "secp256k1", "sha2 0.10.8", + "substrate-bn", ] [[package]] @@ -1957,13 +2353,22 @@ dependencies = [ name = "revm-scroll" version = "0.1.0" dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", "auto_impl", + "cfg-if", "enumn", "once_cell", + "openvm-ecc-guest", + "openvm-pairing", + "openvm-sha2", "revm", "revm-inspector", "revm-primitives", "serde", + "substrate-bn", ] [[package]] @@ -2082,6 +2487,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -2167,6 +2578,24 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_arrays" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.210" @@ -2279,7 +2708,7 @@ dependencies = [ "lazy_static", "num-bigint 0.4.6", "p3-baby-bear", - "p3-field", + "p3-field 0.2.0-succinct", "p3-poseidon2", "p3-symmetric", "serde", @@ -2323,6 +2752,32 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.100", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2528,6 +2983,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unroll" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad948c1cb799b1a70f836077721a92a35ac177d4daddf4c20a633786d4cf618" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d8da513..95e6eb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,27 @@ revm-primitives = { git = "https://github.com/scroll-tech/revm", branch = "feat/ revm-inspector = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } # misc +cfg-if = "1.0" auto_impl = "1.2.0" enumn = { version = "0.1" } once_cell = { version = "1.19", default-features = false, features = ["alloc"] } serde = { version = "1.0", features = ["derive"], optional = true, default-features = false } +# Optionally use substrate implementation for eip1962 +bn = { package = "substrate-bn", version = "0.6", default-features = false, optional = true } + +# Use arkworks implementation for eip1962 +ark-bn254 = { version = "0.5", default-features = false, features = ["curve"] } +ark-ec = { version = "0.5", default-features = false } +ark-ff = { version = "0.5", default-features = false, features = ["asm"] } +ark-serialize = { version = "0.5", default-features = false } + +# openvm +[target.'cfg(all(target_os = "zkvm", not(target_vendor = "succinct"), target_arch = "riscv32"))'.dependencies] +openvm-ecc-guest = { git = "https://github.com/openvm-org/openvm.git", rev = "9ef822c947f14eb436e293971c969cfcb688dbe7" } +openvm-sha2 = { git = "https://github.com/openvm-org/openvm.git", rev = "9ef822c947f14eb436e293971c969cfcb688dbe7" } +openvm-pairing = { git = "https://github.com/openvm-org/openvm.git", rev = "9ef822c947f14eb436e293971c969cfcb688dbe7", features = ["bn254"] } + [features] default = ["std", "c-kzg", "secp256k1", "portable", "blst"] std = ["serde?/std", "revm/std"] @@ -29,3 +45,10 @@ c-kzg = ["revm/c-kzg"] # `kzg-rs` is not audited but useful for `no_std` environment, use it with causing and default to `c-kzg` if possible. kzg-rs = ["revm/kzg-rs"] blst = ["revm/blst"] + +# bn128 related +bn = ["revm/bn", "dep:bn"] +openvm = [] + +[lints.rust] +unexpected-cfgs = "allow" diff --git a/src/lib.rs b/src/lib.rs index 068c990..bf309a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(any(test, target_os = "zkvm")), warn(unused_crate_dependencies))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] diff --git a/src/precompile/bn128.rs b/src/precompile/bn128.rs deleted file mode 100644 index 6f647db..0000000 --- a/src/precompile/bn128.rs +++ /dev/null @@ -1,33 +0,0 @@ -use revm::precompile::{ - bn128::{self, run_pair, PAIR_ELEMENT_LEN}, - PrecompileError, PrecompileResult, PrecompileWithAddress, -}; - -pub mod pair { - use super::*; - - pub use bn128::pair::{ADDRESS, ISTANBUL_PAIR_BASE, ISTANBUL_PAIR_PER_POINT}; - - /// The number of pairing inputs per pairing operation. If the inputs provided to the precompile - /// call are < 4, we append (G1::infinity, G2::generator) until we have the required no. of - /// inputs. - const BERNOULLI_LEN_LIMIT: usize = 4; - - /// The Bn128 pair precompile with BERNOULLI input rules. - pub const BERNOULLI: PrecompileWithAddress = PrecompileWithAddress(ADDRESS, bernoulli_run); - - /// The bernoulli Bn128 pair precompile implementation. - /// - /// # Errors - /// - `PrecompileError::Other("BN128PairingInputOverflow: input overflow".into())` if the input - /// length is greater than 768 bytes. - fn bernoulli_run(input: &[u8], gas_limit: u64) -> PrecompileResult { - if input.len() > BERNOULLI_LEN_LIMIT * PAIR_ELEMENT_LEN { - return Err(PrecompileError::Other("BN128PairingInputOverflow: input overflow".into())); - } - run_pair(input, ISTANBUL_PAIR_PER_POINT, ISTANBUL_PAIR_BASE, gas_limit) - } - - /// The Bn128 pair precompile in FEYNMAN hardfork. - pub const FEYNMAN: PrecompileWithAddress = bn128::pair::ISTANBUL; -} diff --git a/src/precompile/bn128/arkworks.rs b/src/precompile/bn128/arkworks.rs new file mode 100644 index 0000000..c833681 --- /dev/null +++ b/src/precompile/bn128/arkworks.rs @@ -0,0 +1,215 @@ +// Copied from https://github.com/bluealloy/revm/blob/v75/crates/precompile/src/bn128/arkworks.rs under MIT License +use super::{PrecompileError, FQ2_LEN, FQ_LEN, G1_LEN, SCALAR_LEN}; +use std::vec::Vec; + +use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine}; +use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; +use ark_ff::{One, PrimeField, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + +/// Reads a single `Fq` field element from the input slice. +/// +/// Takes a byte slice and attempts to interpret the first 32 bytes as an +/// elliptic curve field element. Returns an error if the bytes do not form +/// a valid field element. +/// +/// # Panics +/// +/// Panics if the input is not at least 32 bytes long. +#[inline] +fn read_fq(input_be: &[u8]) -> Result { + assert_eq!(input_be.len(), FQ_LEN, "input must be {FQ_LEN} bytes"); + + let mut input_le = [0u8; FQ_LEN]; + input_le.copy_from_slice(input_be); + + // Reverse in-place to convert from big-endian to little-endian. + input_le.reverse(); + + Fq::deserialize_uncompressed(&input_le[..]) + .map_err(|_| PrecompileError::Bn128FieldPointNotAMember) +} +/// Reads a Fq2 (quadratic extension field element) from the input slice. +/// +/// Parses two consecutive Fq field elements as the real and imaginary parts +/// of an Fq2 element. +/// The second component is parsed before the first, ie if a we represent an +/// element in Fq2 as (x,y) -- `y` is parsed before `x` +/// +/// # Panics +/// +/// Panics if the input is not at least 64 bytes long. +#[inline] +fn read_fq2(input: &[u8]) -> Result { + let y = read_fq(&input[..FQ_LEN])?; + let x = read_fq(&input[FQ_LEN..2 * FQ_LEN])?; + + Ok(Fq2::new(x, y)) +} + +/// Creates a new `G1` point from the given `x` and `y` coordinates. +/// +/// Constructs a point on the G1 curve from its affine coordinates. +/// +/// Note: The point at infinity which is represented as (0,0) is +/// handled specifically because `AffineG1` is not capable of +/// representing such a point. +/// In particular, when we convert from `AffineG1` to `G1`, the point +/// will be (0,0,1) instead of (0,1,0) +#[inline] +fn new_g1_point(px: Fq, py: Fq) -> Result { + if px.is_zero() && py.is_zero() { + Ok(G1Affine::zero()) + } else { + // We cannot use `G1Affine::new` because that triggers an assert if the point is not on the + // curve. + let point = G1Affine::new_unchecked(px, py); + if !point.is_on_curve() || !point.is_in_correct_subgroup_assuming_on_curve() { + return Err(PrecompileError::Bn128AffineGFailedToCreate); + } + Ok(point) + } +} + +/// Creates a new `G2` point from the given Fq2 coordinates. +/// +/// G2 points in BN128 are defined over a quadratic extension field Fq2. +/// This function takes two Fq2 elements representing the x and y coordinates +/// and creates a G2 point. +/// +/// Note: The point at infinity which is represented as (0,0) is +/// handled specifically because `AffineG2` is not capable of +/// representing such a point. +/// In particular, when we convert from `AffineG2` to `G2`, the point +/// will be (0,0,1) instead of (0,1,0) +#[inline] +fn new_g2_point(x: Fq2, y: Fq2) -> Result { + let point = if x.is_zero() && y.is_zero() { + G2Affine::zero() + } else { + // We cannot use `G1Affine::new` because that triggers an assert if the point is not on the + // curve. + let point = G2Affine::new_unchecked(x, y); + if !point.is_on_curve() || !point.is_in_correct_subgroup_assuming_on_curve() { + return Err(PrecompileError::Bn128AffineGFailedToCreate); + } + point + }; + + Ok(point) +} + +/// Reads a G1 point from the input slice. +/// +/// Parses a G1 point from a byte slice by reading two consecutive field elements +/// representing the x and y coordinates. +/// +/// # Panics +/// +/// Panics if the input is not at least 64 bytes long. +#[inline] +pub(super) fn read_g1_point(input: &[u8]) -> Result { + let px = read_fq(&input[0..FQ_LEN])?; + let py = read_fq(&input[FQ_LEN..2 * FQ_LEN])?; + new_g1_point(px, py) +} + +/// Encodes a G1 point into a byte array. +/// +/// Converts a G1 point in Jacobian coordinates to affine coordinates and +/// serializes the x and y coordinates as big-endian byte arrays. +/// +/// Note: If the point is the point at infinity, this function returns +/// all zeroes. +#[inline] +pub(super) fn encode_g1_point(point: G1Affine) -> [u8; G1_LEN] { + let mut output = [0u8; G1_LEN]; + let Some((x, y)) = point.xy() else { + return output; + }; + + let mut x_bytes = [0u8; FQ_LEN]; + x.serialize_uncompressed(&mut x_bytes[..]).expect("Failed to serialize x coordinate"); + + let mut y_bytes = [0u8; FQ_LEN]; + y.serialize_uncompressed(&mut y_bytes[..]).expect("Failed to serialize x coordinate"); + + // Convert to big endian by reversing the bytes. + x_bytes.reverse(); + y_bytes.reverse(); + + // Place x in the first half, y in the second half. + output[0..FQ_LEN].copy_from_slice(&x_bytes); + output[FQ_LEN..(FQ_LEN * 2)].copy_from_slice(&y_bytes); + + output +} + +/// Reads a G2 point from the input slice. +/// +/// Parses a G2 point from a byte slice by reading four consecutive Fq field elements +/// representing the two Fq2 coordinates (x and y) of the G2 point. +/// +/// # Panics +/// +/// Panics if the input is not at least 128 bytes long. +#[inline] +pub(super) fn read_g2_point(input: &[u8]) -> Result { + let ba = read_fq2(&input[0..FQ2_LEN])?; + let bb = read_fq2(&input[FQ2_LEN..2 * FQ2_LEN])?; + new_g2_point(ba, bb) +} + +/// Reads a scalar from the input slice +/// +/// Note: The scalar does not need to be canonical. +/// +/// # Panics +/// +/// If `input.len()` is not equal to [`SCALAR_LEN`]. +#[inline] +pub(super) fn read_scalar(input: &[u8]) -> Fr { + assert_eq!( + input.len(), + SCALAR_LEN, + "unexpected scalar length. got {}, expected {SCALAR_LEN}", + input.len() + ); + Fr::from_be_bytes_mod_order(input) +} + +/// Performs point addition on two G1 points. +#[inline] +pub(super) fn g1_point_add(p1: G1Affine, p2: G1Affine) -> G1Affine { + let p1_jacobian: G1Projective = p1.into(); + + let p3 = p1_jacobian + p2; + + p3.into_affine() +} + +/// Performs a G1 scalar multiplication. +#[inline] +pub(super) fn g1_point_mul(p: G1Affine, fr: Fr) -> G1Affine { + let big_int = fr.into_bigint(); + let result = p.mul_bigint(big_int); + + result.into_affine() +} + +/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and +/// returns true if the result is equal to the identity element. +/// +/// Note: If the input is empty, this function returns true. +/// This is different to EIP2537 which disallows the empty input. +#[inline] +pub(super) fn pairing_check(pairs: &[(G1Affine, G2Affine)]) -> bool { + if pairs.is_empty() { + return true; + } + + let (g1_points, g2_points): (Vec, Vec) = pairs.iter().copied().unzip(); + + let pairing_result = Bn254::multi_pairing(&g1_points, &g2_points); + pairing_result.0.is_one() +} diff --git a/src/precompile/bn128/mod.rs b/src/precompile/bn128/mod.rs new file mode 100644 index 0000000..c3581d0 --- /dev/null +++ b/src/precompile/bn128/mod.rs @@ -0,0 +1,458 @@ +use revm::precompile::{ + bn128::{self, ADD_INPUT_LEN, MUL_INPUT_LEN, PAIR_ELEMENT_LEN}, + utilities::{bool_to_bytes32, right_pad}, + PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, +}; +use std::vec::Vec; + +cfg_if::cfg_if! { + if #[cfg(all(target_os = "zkvm", not(target_vendor = "succinct"), target_arch = "riscv32", feature = "openvm"))] { + mod openvm; + use openvm::{ + encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point, + read_scalar, + }; + } else if #[cfg(feature = "bn")]{ + mod substrate; + use substrate::{ + encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point, + read_scalar, + }; + use {ark_bn254 as _, ark_ec as _, ark_ff as _, ark_serialize as _}; + } else { + mod arkworks; + use arkworks::{ + encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point, + read_scalar, + }; + } +} + +/// Bn128 add precompile +pub mod add { + use super::*; + + pub use bn128::add::{ADDRESS, ISTANBUL_ADD_GAS_COST}; + + /// Bn128 add precompile with ISTANBUL gas rules + pub const ISTANBUL: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { + run_add(input, ISTANBUL_ADD_GAS_COST, gas_limit) + }); +} + +/// Bn128 mul precompile +pub mod mul { + use super::*; + + pub use bn128::mul::{ADDRESS, ISTANBUL_MUL_GAS_COST}; + + /// Bn128 mul precompile with ISTANBUL gas rules + pub const ISTANBUL: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { + run_mul(input, ISTANBUL_MUL_GAS_COST, gas_limit) + }); +} + +pub mod pair { + use super::*; + + pub use bn128::pair::{ADDRESS, ISTANBUL_PAIR_BASE, ISTANBUL_PAIR_PER_POINT}; + + /// The number of pairing inputs per pairing operation. If the inputs provided to the precompile + /// call are < 4, we append (G1::infinity, G2::generator) until we have the required no. of + /// inputs. + const BERNOULLI_LEN_LIMIT: usize = 4; + + /// The Bn128 pair precompile with BERNOULLI input rules. + pub const BERNOULLI: PrecompileWithAddress = PrecompileWithAddress(ADDRESS, bernoulli_run); + + /// The bernoulli Bn128 pair precompile implementation. + /// + /// # Errors + /// - `PrecompileError::Other("BN128PairingInputOverflow: input overflow".into())` if the input + /// length is greater than 768 bytes. + fn bernoulli_run(input: &[u8], gas_limit: u64) -> PrecompileResult { + if input.len() > BERNOULLI_LEN_LIMIT * PAIR_ELEMENT_LEN { + return Err(PrecompileError::Other("BN128PairingInputOverflow: input overflow".into())); + } + run_pair(input, ISTANBUL_PAIR_PER_POINT, ISTANBUL_PAIR_BASE, gas_limit) + } + + /// The Bn128 pair precompile in FEYNMAN hardfork. + pub const FEYNMAN: PrecompileWithAddress = bn128::pair::ISTANBUL; +} + +// Copied from https://github.com/bluealloy/revm/blob/v75/crates/precompile/src/bn128.rs Under MIT License + +/// FQ_LEN specifies the number of bytes needed to represent an +/// Fq element. This is an element in the base field of BN254. +/// +/// Note: The base field is used to define G1 and G2 elements. +const FQ_LEN: usize = 32; + +/// SCALAR_LEN specifies the number of bytes needed to represent an Fr element. +/// This is an element in the scalar field of BN254. +const SCALAR_LEN: usize = 32; + +/// FQ2_LEN specifies the number of bytes needed to represent an +/// Fq^2 element. +/// +/// Note: This is the quadratic extension of Fq, and by definition +/// means we need 2 Fq elements. +const FQ2_LEN: usize = 2 * FQ_LEN; + +/// G1_LEN specifies the number of bytes needed to represent a G1 element. +/// +/// Note: A G1 element contains 2 Fq elements. +const G1_LEN: usize = 2 * FQ_LEN; +/// G2_LEN specifies the number of bytes needed to represent a G2 element. +/// +/// Note: A G2 element contains 2 Fq^2 elements. +const G2_LEN: usize = 2 * FQ2_LEN; + +/// Run the Bn128 add precompile +pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult { + if gas_cost > gas_limit { + return Err(PrecompileError::OutOfGas); + } + + let input = right_pad::(input); + + let p1 = read_g1_point(&input[..G1_LEN])?; + let p2 = read_g1_point(&input[G1_LEN..])?; + let result = g1_point_add(p1, p2); + + let output = encode_g1_point(result); + + Ok(PrecompileOutput::new(gas_cost, output.into())) +} + +/// Run the Bn128 mul precompile +pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult { + if gas_cost > gas_limit { + return Err(PrecompileError::OutOfGas); + } + + let input = right_pad::(input); + + let p = read_g1_point(&input[..G1_LEN])?; + + let scalar = read_scalar(&input[G1_LEN..G1_LEN + SCALAR_LEN]); + let result = g1_point_mul(p, scalar); + + let output = encode_g1_point(result); + + Ok(PrecompileOutput::new(gas_cost, output.into())) +} + +/// Run the Bn128 pair precompile +pub fn run_pair( + input: &[u8], + pair_per_point_cost: u64, + pair_base_cost: u64, + gas_limit: u64, +) -> PrecompileResult { + let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost; + if gas_used > gas_limit { + return Err(PrecompileError::OutOfGas); + } + + if input.len() % PAIR_ELEMENT_LEN != 0 { + return Err(PrecompileError::Bn128PairLength); + } + + let elements = input.len() / PAIR_ELEMENT_LEN; + + let mut points = Vec::with_capacity(elements); + + for idx in 0..elements { + // Offset to the start of the pairing element at index `idx` in the byte slice + let start = idx * PAIR_ELEMENT_LEN; + let g1_start = start; + // Offset to the start of the G2 element in the pairing element + // This is where G1 ends. + let g2_start = start + G1_LEN; + + let encoded_g1_element = &input[g1_start..g2_start]; + let encoded_g2_element = &input[g2_start..g2_start + G2_LEN]; + + // If either the G1 or G2 element is the encoded representation + // of the point at infinity, then these two points are no-ops + // in the pairing computation. + // + // Note: we do not skip the validation of these two elements even if + // one of them is the point at infinity because we could have G1 be + // the point at infinity and G2 be an invalid element or vice versa. + // In that case, the precompile should error because one of the elements + // was invalid. + let g1_is_zero = encoded_g1_element.iter().all(|i| *i == 0); + let g2_is_zero = encoded_g2_element.iter().all(|i| *i == 0); + + // Get G1 and G2 points from the input + let a = read_g1_point(encoded_g1_element)?; + let b = read_g2_point(encoded_g2_element)?; + + if !g1_is_zero && !g2_is_zero { + points.push((a, b)); + } + } + + let success = pairing_check(&points); + + Ok(PrecompileOutput::new(gas_used, bool_to_bytes32(success))) +} + +#[cfg(test)] +mod tests { + use revm::precompile::{ + bn128::{ + add::BYZANTIUM_ADD_GAS_COST, + mul::BYZANTIUM_MUL_GAS_COST, + pair::{BYZANTIUM_PAIR_BASE, BYZANTIUM_PAIR_PER_POINT}, + }, + PrecompileError, + }; + use revm_primitives::hex; + + use super::*; + + #[test] + fn test_alt_bn128_add() { + let input = hex::decode( + "\ + 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\ + 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\ + 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\ + 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + ) + .unwrap(); + let expected = hex::decode( + "\ + 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\ + 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + ) + .unwrap(); + + let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Zero sum test + let input = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Out of gas test + let input = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 499); + + assert!(matches!(res, Err(PrecompileError::OutOfGas))); + + // No input test + let input = [0u8; 0]; + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + + let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500); + assert!(matches!(res, Err(PrecompileError::Bn128AffineGFailedToCreate))); + } + + #[test] + fn test_alt_bn128_mul() { + let input = hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2", + ) + .unwrap(); + let expected = hex::decode( + "\ + 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ + 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + ) + .unwrap(); + + let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Out of gas test + let input = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0200000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 39_999); + assert!(matches!(res, Err(PrecompileError::OutOfGas))); + + // Zero multiplication test + let input = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0200000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); + assert_eq!(outcome.bytes, expected); + + // No input test + let input = [0u8; 0]; + let expected = hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 0f00000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + + let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000); + assert!(matches!(res, Err(PrecompileError::Bn128AffineGFailedToCreate))); + } + + #[test] + fn test_alt_bn128_pair() { + let input = hex::decode( + "\ + 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ + 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ + 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ + 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ + 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ + 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ + 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ + 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ) + .unwrap(); + let expected = + hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(); + + let outcome = + run_pair(&input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 260_000).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Out of gas test + let input = hex::decode( + "\ + 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ + 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ + 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ + 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ + 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ + 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ + 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ + 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ) + .unwrap(); + + let res = run_pair(&input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 259_999); + assert!(matches!(res, Err(PrecompileError::OutOfGas))); + + // No input test + let input = [0u8; 0]; + let expected = + hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(); + + let outcome = + run_pair(&input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 260_000).unwrap(); + assert_eq!(outcome.bytes, expected); + + // Point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + + let res = run_pair(&input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 260_000); + assert!(matches!(res, Err(PrecompileError::Bn128AffineGFailedToCreate))); + + // Invalid input length + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111\ + ", + ) + .unwrap(); + + let res = run_pair(&input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 260_000); + assert!(matches!(res, Err(PrecompileError::Bn128PairLength))); + } +} diff --git a/src/precompile/bn128/openvm.rs b/src/precompile/bn128/openvm.rs new file mode 100644 index 0000000..5e19fdf --- /dev/null +++ b/src/precompile/bn128/openvm.rs @@ -0,0 +1,174 @@ +// Copied from https://github.com/axiom-crypto/revm/blob/v75-openvm/crates/precompile/src/bn128.rs under MIT License +use revm::precompile::PrecompileError; +use std::vec::Vec; + +use openvm_ecc_guest::{ + algebra::IntMod, + weierstrass::{IntrinsicCurve, WeierstrassPoint}, + AffinePoint, +}; +use openvm_pairing::{ + bn254::{Bn254, Fp, Fp2, G1Affine, G2Affine, Scalar}, + PairingCheck, +}; + +/// FQ_LEN specifies the number of bytes needed to represent an +/// Fq element. This is an element in the base field of BN254. +/// +/// Note: The base field is used to define G1 and G2 elements. +const FQ_LEN: usize = 32; + +/// SCALAR_LEN specifies the number of bytes needed to represent an Fr element. +/// This is an element in the scalar field of BN254. +const SCALAR_LEN: usize = 32; + +/// FQ2_LEN specifies the number of bytes needed to represent an +/// Fq^2 element. +/// +/// Note: This is the quadratic extension of Fq, and by definition +/// means we need 2 Fq elements. +const FQ2_LEN: usize = 2 * FQ_LEN; + +/// G1_LEN specifies the number of bytes needed to represent a G1 element. +/// +/// Note: A G1 element contains 2 Fq elements. +const G1_LEN: usize = 2 * FQ_LEN; + +#[inline] +fn read_fq(input: &[u8]) -> Result { + if input.len() < FQ_LEN { + Err(PrecompileError::Bn128FieldPointNotAMember) + } else { + Fp::from_be_bytes(&input[..32]).ok_or(PrecompileError::Bn128FieldPointNotAMember) + } +} + +/// Reads a Fq2 (quadratic extension field element) from the input slice. +/// +/// Parses two consecutive Fq field elements as the real and imaginary parts +/// of an Fq2 element. +/// The second component is parsed before the first, ie if a we represent an +/// element in Fq2 as (x,y) -- `y` is parsed before `x` +/// +/// # Panics +/// +/// Panics if the input is not at least 64 bytes long. +#[inline] +fn read_fq2(input: &[u8]) -> Result { + let y = read_fq(&input[..FQ_LEN])?; + let x = read_fq(&input[FQ_LEN..2 * FQ_LEN])?; + Ok(Fp2::new(x, y)) +} + +#[inline] +fn new_g1_affine_point(px: Fp, py: Fp) -> Result { + G1Affine::from_xy(px, py).ok_or(PrecompileError::Bn128AffineGFailedToCreate) +} + +/// Reads a G1 point from the input slice. +/// +/// Parses a G1 point from a byte slice by reading two consecutive field elements +/// representing the x and y coordinates. +/// +/// # Panics +/// +/// Panics if the input is not at least 64 bytes long. +#[inline] +pub(super) fn read_g1_point(input: &[u8]) -> Result { + let px = read_fq(&input[0..FQ_LEN])?; + let py = read_fq(&input[FQ_LEN..2 * FQ_LEN])?; + new_g1_affine_point(px, py) +} + +/// Encodes a G1 point into a byte array. +/// +/// Converts a G1 point in Jacobian coordinates to affine coordinates and +/// serializes the x and y coordinates as big-endian byte arrays. +/// +/// Note: If the point is the point at infinity, this function returns +/// all zeroes. +#[inline] +pub(super) fn encode_g1_point(point: G1Affine) -> [u8; G1_LEN] { + let mut output = [0u8; G1_LEN]; + + // manually reverse to avoid allocation + let x_bytes: &[u8] = point.x().as_le_bytes(); + let y_bytes: &[u8] = point.y().as_le_bytes(); + for i in 0..FQ_LEN { + output[i] = x_bytes[FQ_LEN - 1 - i]; + output[i + FQ_LEN] = y_bytes[FQ_LEN - 1 - i]; + } + output +} + +/// Reads a G2 point from the input slice. +/// +/// Parses a G2 point from a byte slice by reading four consecutive Fq field elements +/// representing the two Fq2 coordinates (x and y) of the G2 point. +/// +/// # Panics +/// +/// Panics if the input is not at least 128 bytes long. +#[inline] +pub(super) fn read_g2_point(input: &[u8]) -> Result { + let ba = read_fq2(&input[0..FQ2_LEN])?; + let bb = read_fq2(&input[FQ2_LEN..2 * FQ2_LEN])?; + + G2Affine::from_xy(ba, bb).ok_or(PrecompileError::Bn128AffineGFailedToCreate) +} + +/// Reads a scalar from the input slice +/// +/// Note: The scalar does not need to be canonical. +/// +/// # Panics +/// +/// If `input.len()` is not equal to [`SCALAR_LEN`]. +#[inline] +pub(super) fn read_scalar(input: &[u8]) -> Scalar { + assert_eq!( + input.len(), + SCALAR_LEN, + "unexpected scalar length. got {}, expected {SCALAR_LEN}", + input.len() + ); + Scalar::from_be_bytes_unchecked(input) +} + +/// Performs point addition on two G1 points. +#[inline] +pub(super) fn g1_point_add(p1: G1Affine, p2: G1Affine) -> G1Affine { + p1 + p2 +} + +/// Performs a G1 scalar multiplication. +#[inline] +pub(super) fn g1_point_mul(p: G1Affine, fr: Scalar) -> G1Affine { + Bn254::msm(&[fr], &[p]) +} + +/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and +/// returns true if the result is equal to the identity element. +/// +/// Note: If the input is empty, this function returns true. +/// This is different to EIP2537 which disallows the empty input. +#[inline] +pub(super) fn pairing_check(pairs: &[(G1Affine, G2Affine)]) -> bool { + if pairs.is_empty() { + return true; + } + let (g1_points, g2_points): (Vec<_>, Vec<_>) = pairs + .iter() + .cloned() + .map(|(g1, g2)| { + let (g1_x, g1_y) = g1.into_coords(); + let g1 = AffinePoint::new(g1_x, g1_y); + + let (g2_x, g2_y) = g2.into_coords(); + let g2 = AffinePoint::new(g2_x, g2_y); + (g1, g2) + }) + .unzip(); + + Bn254::pairing_check(&g1_points, &g2_points).is_ok() +} diff --git a/src/precompile/bn128/substrate.rs b/src/precompile/bn128/substrate.rs new file mode 100644 index 0000000..0395a17 --- /dev/null +++ b/src/precompile/bn128/substrate.rs @@ -0,0 +1,168 @@ +// Copied from https://github.com/bluealloy/revm/blob/v75/crates/precompile/src/bn128/substrate.rs under MIT License +use super::{PrecompileError, FQ2_LEN, FQ_LEN, G1_LEN, SCALAR_LEN}; +use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; + +/// Reads a single `Fq` field element from the input slice. +/// +/// Takes a byte slice and attempts to interpret the first 32 bytes as an +/// elliptic curve field element. Returns an error if the bytes do not form +/// a valid field element. +/// +/// # Panics +/// +/// Panics if the input is not at least 32 bytes long. +#[inline] +fn read_fq(input: &[u8]) -> Result { + Fq::from_slice(&input[..FQ_LEN]).map_err(|_| PrecompileError::Bn128FieldPointNotAMember) +} +/// Reads a Fq2 (quadratic extension field element) from the input slice. +/// +/// Parses two consecutive Fq field elements as the real and imaginary parts +/// of an Fq2 element. +/// The second component is parsed before the first, ie if a we represent an +/// element in Fq2 as (x,y) -- `y` is parsed before `x` +/// +/// # Panics +/// +/// Panics if the input is not at least 64 bytes long. +#[inline] +fn read_fq2(input: &[u8]) -> Result { + let y = read_fq(&input[..FQ_LEN])?; + let x = read_fq(&input[FQ_LEN..2 * FQ_LEN])?; + Ok(Fq2::new(x, y)) +} + +/// Creates a new `G1` point from the given `x` and `y` coordinates. +/// +/// Constructs a point on the G1 curve from its affine coordinates. +/// +/// Note: The point at infinity which is represented as (0,0) is +/// handled specifically because `AffineG1` is not capable of +/// representing such a point. +/// In particular, when we convert from `AffineG1` to `G1`, the point +/// will be (0,0,1) instead of (0,1,0) +#[inline] +fn new_g1_point(px: Fq, py: Fq) -> Result { + if px == Fq::zero() && py == Fq::zero() { + Ok(G1::zero()) + } else { + AffineG1::new(px, py) + .map(Into::into) + .map_err(|_| PrecompileError::Bn128AffineGFailedToCreate) + } +} + +/// Creates a new `G2` point from the given Fq2 coordinates. +/// +/// G2 points in BN128 are defined over a quadratic extension field Fq2. +/// This function takes two Fq2 elements representing the x and y coordinates +/// and creates a G2 point. +/// +/// Note: The point at infinity which is represented as (0,0) is +/// handled specifically because `AffineG2` is not capable of +/// representing such a point. +/// In particular, when we convert from `AffineG2` to `G2`, the point +/// will be (0,0,1) instead of (0,1,0) +#[inline] +fn new_g2_point(x: Fq2, y: Fq2) -> Result { + let point = if x.is_zero() && y.is_zero() { + G2::zero() + } else { + G2::from(AffineG2::new(x, y).map_err(|_| PrecompileError::Bn128AffineGFailedToCreate)?) + }; + + Ok(point) +} + +/// Reads a G1 point from the input slice. +/// +/// Parses a G1 point from a byte slice by reading two consecutive field elements +/// representing the x and y coordinates. +/// +/// # Panics +/// +/// Panics if the input is not at least 64 bytes long. +#[inline] +pub(super) fn read_g1_point(input: &[u8]) -> Result { + let px = read_fq(&input[0..FQ_LEN])?; + let py = read_fq(&input[FQ_LEN..2 * FQ_LEN])?; + new_g1_point(px, py) +} + +/// Encodes a G1 point into a byte array. +/// +/// Converts a G1 point in Jacobian coordinates to affine coordinates and +/// serializes the x and y coordinates as big-endian byte arrays. +/// +/// Note: If the point is the point at infinity, this function returns +/// all zeroes. +#[inline] +pub(super) fn encode_g1_point(point: G1) -> [u8; G1_LEN] { + let mut output = [0u8; G1_LEN]; + + if let Some(point_affine) = AffineG1::from_jacobian(point) { + point_affine.x().to_big_endian(&mut output[..FQ_LEN]).unwrap(); + point_affine.y().to_big_endian(&mut output[FQ_LEN..]).unwrap(); + } + + output +} + +/// Reads a G2 point from the input slice. +/// +/// Parses a G2 point from a byte slice by reading four consecutive Fq field elements +/// representing the two Fq2 coordinates (x and y) of the G2 point. +/// +/// # Panics +/// +/// Panics if the input is not at least 128 bytes long. +#[inline] +pub(super) fn read_g2_point(input: &[u8]) -> Result { + let ba = read_fq2(&input[0..FQ2_LEN])?; + let bb = read_fq2(&input[FQ2_LEN..2 * FQ2_LEN])?; + new_g2_point(ba, bb) +} + +/// Reads a scalar from the input slice +/// +/// Note: The scalar does not need to be canonical. +/// +/// # Panics +/// +/// If `input.len()` is not equal to [`SCALAR_LEN`]. +#[inline] +pub(super) fn read_scalar(input: &[u8]) -> bn::Fr { + assert_eq!( + input.len(), + SCALAR_LEN, + "unexpected scalar length. got {}, expected {SCALAR_LEN}", + input.len() + ); + // `Fr::from_slice` can only fail when the length is not `SCALAR_LEN`. + bn::Fr::from_slice(input).unwrap() +} + +/// Performs point addition on two G1 points. +#[inline] +pub(super) fn g1_point_add(p1: G1, p2: G1) -> G1 { + p1 + p2 +} + +/// Performs a G1 scalar multiplication. +#[inline] +pub(super) fn g1_point_mul(p: G1, fr: bn::Fr) -> G1 { + p * fr +} + +/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and +/// returns true if the result is equal to the identity element. +/// +/// Note: If the input is empty, this function returns true. +/// This is different to EIP2537 which disallows the empty input. +#[inline] +pub(super) fn pairing_check(pairs: &[(G1, G2)]) -> bool { + if pairs.is_empty() { + return true; + } + bn::pairing_batch(pairs) == Gt::one() +} diff --git a/src/precompile/hash.rs b/src/precompile/hash.rs index 1d84dce..3845517 100644 --- a/src/precompile/hash.rs +++ b/src/precompile/hash.rs @@ -1,7 +1,7 @@ use super::precompile_not_implemented; use revm::{ - precompile::{hash, PrecompileWithAddress}, + precompile::{hash, PrecompileResult, PrecompileWithAddress}, primitives::Address, }; @@ -15,7 +15,24 @@ pub mod sha256 { pub const SHANGHAI: PrecompileWithAddress = precompile_not_implemented(ADDRESS); /// The bernoulli SHA256 precompile implementation with address. - pub const BERNOULLI: PrecompileWithAddress = PrecompileWithAddress(ADDRESS, hash::sha256_run); + pub const BERNOULLI: PrecompileWithAddress = PrecompileWithAddress(ADDRESS, run); + + pub fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { + cfg_if::cfg_if! { + if #[cfg(all(target_os = "zkvm", not(target_vendor = "succinct"), target_arch = "riscv32", feature = "openvm"))] { + use revm::precompile::{calc_linear_cost_u32, PrecompileError, PrecompileOutput}; + let cost = calc_linear_cost_u32(input.len(), 60, 12); + if cost > gas_limit { + Err(PrecompileError::OutOfGas) + } else { + let output = openvm_sha2::sha256(input); + Ok(PrecompileOutput::new(cost, output.to_vec().into())) + } + } else { + hash::sha256_run(input, gas_limit) + } + } + } } pub mod ripemd160 { diff --git a/src/precompile/mod.rs b/src/precompile/mod.rs index 7121d8c..2edad38 100644 --- a/src/precompile/mod.rs +++ b/src/precompile/mod.rs @@ -11,10 +11,10 @@ use revm::{ }; use revm_primitives::hardfork::SpecId; -mod blake2; -mod bn128; -mod hash; -mod modexp; +pub mod blake2; +pub mod bn128; +pub mod hash; +pub mod modexp; /// Provides Scroll precompiles, modifying any relevant behaviour. #[derive(Debug, Clone)] @@ -62,8 +62,8 @@ pub(crate) fn pre_bernoulli() -> &'static Precompiles { hash::ripemd160::SHANGHAI, precompile::identity::FUN, modexp::BERNOULLI, - precompile::bn128::add::ISTANBUL, - precompile::bn128::mul::ISTANBUL, + bn128::add::ISTANBUL, + bn128::mul::ISTANBUL, bn128::pair::BERNOULLI, blake2::SHANGHAI, ]);