init commit

This commit is contained in:
zhenfei
2023-11-28 18:42:25 -05:00
commit 8f7b988424
15 changed files with 2969 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

993
Cargo.lock generated Normal file
View File

@@ -0,0 +1,993 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "ark-std"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185"
dependencies = [
"colored",
"num-traits",
"rand",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "blake2b_simd"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
dependencies = [
"anstyle",
"clap_lex",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "colored"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
dependencies = [
"is-terminal",
"lazy_static",
"windows-sys 0.48.0",
]
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "ff"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
"bitvec",
"rand_core",
"subtle",
]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "getrandom"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "goldilocks"
version = "0.1.0"
dependencies = [
"ark-std",
"criterion",
"ff",
"halo2curves",
"rand_core",
"rand_xorshift",
"serde",
"subtle",
]
[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core",
"subtle",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "halo2curves"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b1142bd1059aacde1b477e0c80c142910f1ceae67fc619311d6a17428007ab"
dependencies = [
"blake2b_simd",
"ff",
"group",
"lazy_static",
"num-bigint",
"num-traits",
"pasta_curves",
"paste",
"rand",
"rand_core",
"static_assertions",
"subtle",
]
[[package]]
name = "hermit-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "js-sys"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin",
]
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[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",
"static_assertions",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_xorshift"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
"rand_core",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustix"
version = "0.38.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "web-sys"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]

24
Cargo.toml Normal file
View File

@@ -0,0 +1,24 @@
[package]
name = "goldilocks"
version = "0.1.0"
edition = "2021"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ff = "0.13.0"
rand_core = "0.6.4"
subtle = "2.5"
serde = "1.0"
[dev-dependencies]
rand_xorshift = "0.3"
ark-std = { version = "0.4", features = ["print-trace"] }
halo2curves = "0.1.0"
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "bench"
harness = false

8
README.md Normal file
View File

@@ -0,0 +1,8 @@
Implementation of Goldilocks and its extension fields
------
This repo implements
- Goldilocks Field mod `2^64 - 2^32 + 1`
- Goldilocks quadratic extension over `x^2 + 1`
- Goldilocks cubic extension over `x^3 - x - 1`
Traits are compatible with `ff 0.13.0`.

51
benches/bench.rs Normal file
View File

@@ -0,0 +1,51 @@
use criterion::{criterion_group, criterion_main, Criterion};
use ff::Field;
use goldilocks::{Goldilocks, GoldilocksExt2, GoldilocksExt3, SmallField};
use halo2curves::bn256::Fr;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
const SIZE: usize = 1000;
criterion_main!(bench);
criterion_group!(bench, bench_fields);
fn bench_fields(c: &mut Criterion) {
bench_field::<Goldilocks>(c, Goldilocks::NAME);
bench_field::<GoldilocksExt2>(c, GoldilocksExt2::NAME);
bench_field::<GoldilocksExt3>(c, GoldilocksExt3::NAME);
bench_field::<Fr>(c, "Bn256 scalar")
}
fn bench_field<F: Field>(c: &mut Criterion, field_name: &str) {
let mut bench_group = c.benchmark_group(format!("{}", field_name));
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let a = (0..SIZE).map(|_| F::random(&mut rng)).collect::<Vec<_>>();
let b = (0..SIZE).map(|_| F::random(&mut rng)).collect::<Vec<_>>();
let bench_str = format!("{} additions", SIZE);
bench_group.bench_function(bench_str, |bencher| {
bencher.iter(|| {
a.iter()
.zip(b.iter())
.map(|(&ai, &bi)| ai + bi)
.collect::<Vec<_>>()
})
});
let bench_str = format!("{} multiplications", SIZE);
bench_group.bench_function(bench_str, |bencher| {
bencher.iter(|| {
a.iter()
.zip(b.iter())
.map(|(&ai, &bi)| ai * bi)
.collect::<Vec<_>>()
})
});
bench_group.finish();
}

20
src/field.rs Normal file
View File

@@ -0,0 +1,20 @@
//! This module defines our customized field trait.
use ff::PrimeField;
use serde::Serialize;
use crate::{fp2::GoldilocksExt2, Goldilocks, GoldilocksExt3};
pub trait SmallField: PrimeField + Serialize {
const NAME: &'static str;
}
impl SmallField for Goldilocks {
const NAME: &'static str = "Goldilocks";
}
impl SmallField for GoldilocksExt2 {
const NAME: &'static str = "GoldilocksExt2";
}
impl SmallField for GoldilocksExt3 {
const NAME: &'static str = "GoldilocksExt3";
}

456
src/fp.rs Normal file
View File

@@ -0,0 +1,456 @@
use crate::util::{add_no_canonicalize_trashing_input, branch_hint, split, sqrt_tonelli_shanks};
use crate::util::{assume, try_inverse_u64};
use core::iter::{Product, Sum};
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use ff::{Field, PrimeField};
use rand_core::RngCore;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
/// Goldilocks field with modulus 2^64 - 2^32 + 1.
/// A Goldilocks field may store a non-canonical form of the element
/// where the value can be between 0 and 2^64.
/// For unique representation of its form, use `to_canonical_u64`
#[derive(Clone, Copy, Debug, Default, Eq, Serialize, Deserialize)]
pub struct Goldilocks(pub(crate) u64);
impl PartialEq for Goldilocks {
fn eq(&self, other: &Goldilocks) -> bool {
self.to_canonical_u64() == other.to_canonical_u64()
}
}
impl Display for Goldilocks {
fn fmt(&self, w: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(w, "{}", self.0)
}
}
/// 2^64 - 2^32 + 1
pub const MODULUS: u64 = 0xffffffff00000001;
/// 2^32 - 1
pub const EPSILON: u64 = 0xffffffff;
impl Field for Goldilocks {
/// The zero element of the field, the additive identity.
const ZERO: Self = Self(0);
/// The one element of the field, the multiplicative identity.
const ONE: Self = Self(1);
/// Returns an element chosen uniformly at random using a user-provided RNG.
/// Note: this sampler is not constant time!
fn random(mut rng: impl RngCore) -> Self {
let mut res = rng.next_u64();
while res >= MODULUS {
res = rng.next_u64();
}
Self(res)
}
/// Squares this element.
#[must_use]
fn square(&self) -> Self {
*self * *self
}
/// Cubes this element.
#[must_use]
fn cube(&self) -> Self {
self.square() * self
}
/// Doubles this element.
#[must_use]
fn double(&self) -> Self {
*self + *self
}
/// Computes the multiplicative inverse of this element,
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
match try_inverse_u64(&self.0) {
Some(p) => CtOption::new(Self(p), Choice::from(1)),
None => CtOption::new(Self(0), Choice::from(0)),
}
}
/// Returns the square root of the field element, if it is
/// quadratic residue.
fn sqrt(&self) -> CtOption<Self> {
// TODO: better algorithm taking advantage of (t-1)/2 has a nice structure
sqrt_tonelli_shanks(self, 0x7fffffff)
}
/// Computes:
///
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
/// field;
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
/// a nonsquare in the field;
///
/// where $G_S$ is a non-square.
///
/// # Warnings
///
/// - The choice of root from `sqrt` is unspecified.
/// - The value of $G_S$ is unspecified, and cannot be assumed to have any specific
/// value in a generic context.
fn sqrt_ratio(_: &Self, _: &Self) -> (Choice, Self) {
unimplemented!()
}
}
impl AsRef<u64> for Goldilocks {
fn as_ref(&self) -> &u64 {
&self.0
}
}
impl AsMut<[u8]> for Goldilocks {
fn as_mut(&mut self) -> &mut [u8] {
let ptr = self as *mut Self as *mut u8;
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
// with alignment greater than that of u8
unsafe { std::slice::from_raw_parts_mut(ptr, 8) }
}
}
impl AsRef<[u8]> for Goldilocks {
fn as_ref(&self) -> &[u8] {
let ptr = self as *const Self as *const u8;
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
// with alignment greater than that of u8
unsafe { std::slice::from_raw_parts(ptr, 8) }
}
}
/// This represents an element of a prime field.
impl PrimeField for Goldilocks {
/// The prime field can be converted back and forth into this binary
/// representation.
type Repr = Self;
/// Modulus of the field written as a string for debugging purposes.
///
/// The encoding of the modulus is implementation-specific. Generic users of the
/// `PrimeField` trait should treat this string as opaque.
const MODULUS: &'static str = "0xffffffff00000001";
/// How many bits are needed to represent an element of this field.
const NUM_BITS: u32 = 64;
/// How many bits of information can be reliably stored in the field element.
///
/// This is usually `Self::NUM_BITS - 1`.
const CAPACITY: u32 = 63;
/// An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd.
///
/// This is the number of leading zero bits in the little-endian bit representation of
/// `modulus - 1`.
const S: u32 = 32;
/// Inverse of $2$ in the field.
const TWO_INV: Self = Self(0x7fffffff80000001);
/// A fixed multiplicative generator of `modulus - 1` order. This element must also be
/// a quadratic nonresidue.
///
/// It can be calculated using [SageMath] as `GF(modulus).primitive_element()`.
///
/// Implementations of this trait MUST ensure that this is the generator used to
/// derive `Self::ROOT_OF_UNITY`.
///
/// [SageMath]: https://www.sagemath.org/
const MULTIPLICATIVE_GENERATOR: Self = Self(7);
/// The `2^s` root of unity.
///
/// It can be calculated by exponentiating `Self::MULTIPLICATIVE_GENERATOR` by `t`,
/// where `t = (modulus - 1) >> Self::S`.
const ROOT_OF_UNITY: Self = Self(0x185629dcda58878c);
/// Inverse of [`Self::ROOT_OF_UNITY`].
const ROOT_OF_UNITY_INV: Self = Self(0x76b6b635b6fc8719);
/// Generator of the `t-order` multiplicative subgroup.
///
/// It can be calculated by exponentiating [`Self::MULTIPLICATIVE_GENERATOR`] by `2^s`,
/// where `s` is [`Self::S`].
const DELTA: Self = Self(0xaa5b2509f86bb4d4);
/// Attempts to convert a byte representation of a field element into an element of
/// this prime field, failing if the input is not canonical (is not smaller than the
/// field's modulus).
///
/// The byte representation is interpreted with the same endianness as elements
/// returned by [`PrimeField::to_repr`].
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
CtOption::new(repr, Choice::from(1))
}
/// Attempts to convert a byte representation of a field element into an element of
/// this prime field, failing if the input is not canonical (is not smaller than the
/// field's modulus).
///
/// The byte representation is interpreted with the same endianness as elements
/// returned by [`PrimeField::to_repr`].
///
/// # Security
///
/// This method provides **no** constant-time guarantees. Implementors of the
/// `PrimeField` trait **may** optimise this method using non-constant-time logic.
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
Self::from_repr(repr).into()
}
/// Converts an element of the prime field into the standard byte representation for
/// this field.
///
/// The endianness of the byte representation is implementation-specific. Generic
/// encodings of field elements should be treated as opaque.
fn to_repr(&self) -> Self::Repr {
*self
}
/// Returns true iff this element is odd.
fn is_odd(&self) -> Choice {
Choice::from((self.0 & 1) as u8)
}
}
impl From<u64> for Goldilocks {
fn from(input: u64) -> Self {
Self(input)
}
}
impl From<Goldilocks> for u64 {
fn from(input: Goldilocks) -> Self {
input.0
}
}
impl ConditionallySelectable for Goldilocks {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self(u64::conditional_select(&a.0, &b.0, choice))
}
}
impl ConstantTimeEq for Goldilocks {
fn ct_eq(&self, other: &Self) -> Choice {
self.to_canonical_u64().ct_eq(&other.to_canonical_u64())
}
}
impl Neg for Goldilocks {
type Output = Self;
#[inline]
fn neg(self) -> Self {
if self.0 == 0 {
self
} else {
Self(MODULUS - self.to_canonical_u64())
}
}
}
impl Add for Goldilocks {
type Output = Self;
#[inline]
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self::Output {
let (sum, over) = self.0.overflowing_add(rhs.0);
let (mut sum, over) = sum.overflowing_add((over as u64) * EPSILON);
if over {
// NB: self.0 > Self::ORDER && rhs.0 > Self::ORDER is necessary but not sufficient for
// double-overflow.
// This assume does two things:
// 1. If compiler knows that either self.0 or rhs.0 <= ORDER, then it can skip this
// check.
// 2. Hints to the compiler how rare this double-overflow is (thus handled better with
// a branch).
assume(self.0 > MODULUS && rhs.0 > MODULUS);
branch_hint();
sum += EPSILON; // Cannot overflow.
}
Self(sum)
}
}
impl<'a> Add<&'a Goldilocks> for Goldilocks {
type Output = Self;
#[inline]
fn add(self, rhs: &'a Goldilocks) -> Self::Output {
self + *rhs
}
}
impl AddAssign for Goldilocks {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<'a> AddAssign<&'a Goldilocks> for Goldilocks {
#[inline]
fn add_assign(&mut self, rhs: &'a Goldilocks) {
*self = *self + *rhs;
}
}
impl Sub for Goldilocks {
type Output = Self;
#[inline]
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: Self) -> Self {
let (diff, under) = self.0.overflowing_sub(rhs.0);
let (mut diff, under) = diff.overflowing_sub((under as u64) * EPSILON);
if under {
// NB: self.0 < EPSILON - 1 && rhs.0 > Self::ORDER is necessary but not sufficient for
// double-underflow.
// This assume does two things:
// 1. If compiler knows that either self.0 >= EPSILON - 1 or rhs.0 <= ORDER, then it
// can skip this check.
// 2. Hints to the compiler how rare this double-underflow is (thus handled better
// with a branch).
assume(self.0 < EPSILON - 1 && rhs.0 > MODULUS);
branch_hint();
diff -= EPSILON; // Cannot underflow.
}
Self(diff)
}
}
impl<'a> Sub<&'a Goldilocks> for Goldilocks {
type Output = Self;
#[inline]
fn sub(self, rhs: &'a Goldilocks) -> Self::Output {
self - *rhs
}
}
impl SubAssign for Goldilocks {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<'a> SubAssign<&'a Goldilocks> for Goldilocks {
#[inline]
fn sub_assign(&mut self, rhs: &'a Goldilocks) {
*self = *self - *rhs;
}
}
impl<T: ::core::borrow::Borrow<Goldilocks>> Sum<T> for Goldilocks {
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, item| acc + item.borrow())
}
}
impl Mul for Goldilocks {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
reduce128((self.0 as u128) * (rhs.0 as u128))
}
}
impl<'a> Mul<&'a Goldilocks> for Goldilocks {
type Output = Self;
#[inline]
fn mul(self, rhs: &'a Goldilocks) -> Self::Output {
self * *rhs
}
}
impl MulAssign for Goldilocks {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<'a> MulAssign<&'a Goldilocks> for Goldilocks {
#[inline]
fn mul_assign(&mut self, rhs: &'a Goldilocks) {
*self = *self * *rhs;
}
}
impl<T: ::core::borrow::Borrow<Goldilocks>> Product<T> for Goldilocks {
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, item| acc * item.borrow())
}
}
/// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the
/// field order and `2^64`.
#[inline]
fn reduce128(x: u128) -> Goldilocks {
let (x_lo, x_hi) = split(x); // This is a no-op
let x_hi_hi = x_hi >> 32;
let x_hi_lo = x_hi & EPSILON;
let (mut t0, borrow) = x_lo.overflowing_sub(x_hi_hi);
if borrow {
branch_hint(); // A borrow is exceedingly rare. It is faster to branch.
t0 -= EPSILON; // Cannot underflow.
}
let t1 = x_hi_lo * EPSILON;
let t2 = unsafe { add_no_canonicalize_trashing_input(t0, t1) };
Goldilocks(t2)
}
impl Goldilocks {
#[inline]
pub fn to_canonical_u64(self) -> u64 {
let mut c = self.0;
// We only need one condition subtraction, since 2 * ORDER would not fit in a u64.
if c >= MODULUS {
c -= MODULUS;
}
c
}
pub const fn size() -> usize {
8
}
pub fn legendre(&self) -> LegendreSymbol {
// s = self^((modulus - 1) // 2)
// 9223372034707292160
let s = 0x7fffffff80000000;
let s = self.pow_vartime([s]);
if s == Self::ZERO {
LegendreSymbol::Zero
} else if s == Self::ONE {
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum LegendreSymbol {
Zero = 0,
QuadraticResidue = 1,
QuadraticNonResidue = -1,
}

366
src/fp2.rs Normal file
View File

@@ -0,0 +1,366 @@
//! This module implements Goldilocks quadratic extension field mod x^2 + 1
use crate::Goldilocks;
use core::iter::{Product, Sum};
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use ff::{Field, PrimeField};
use rand_core::RngCore;
use serde::{Deserialize, Serialize};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
/// Degree 3 Goldilocks extension field mod x^2 + 1
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GoldilocksExt2(pub [Goldilocks; 2]);
/// For a = (a1, a2) and b = (b1, b2)
/// The multiplication is define as
/// c := a * b = a(x) * b(x) % (x^2 + 1)
/// = x*a2*b1 + x*a1*b2
/// + a1*b1 - a2*b2
/// This requires 9 multiplications and 6 1 additions
fn mul_internal(a: &GoldilocksExt2, b: &GoldilocksExt2) -> GoldilocksExt2 {
// todo: optimizations?
let a1b1 = a.0[0] * b.0[0];
let a1b2 = a.0[0] * b.0[1];
let a2b1 = a.0[1] * b.0[0];
let a2b2 = a.0[1] * b.0[1];
let c1 = a1b1 - a2b2;
let c2 = a2b1 + a1b2;
GoldilocksExt2([c1, c2])
}
impl GoldilocksExt2 {
fn to_canonical_u64_array(&self) -> [u64; 2] {
[self.0[0].to_canonical_u64(), self.0[1].to_canonical_u64()]
}
}
impl Field for GoldilocksExt2 {
/// The zero element of the field, the additive identity.
const ZERO: Self = Self([Goldilocks::ZERO; 2]);
/// The one element of the field, the multiplicative identity.
const ONE: Self = Self([Goldilocks::ONE, Goldilocks::ZERO]);
/// Returns an element chosen uniformly at random using a user-provided RNG.
/// Note: this sampler is not constant time!
fn random(mut rng: impl RngCore) -> Self {
let a1 = Goldilocks::random(&mut rng);
let a2 = Goldilocks::random(&mut rng);
Self([a1, a2])
}
/// Squares this element.
#[must_use]
fn square(&self) -> Self {
*self * *self
}
/// Cubes this element.
#[must_use]
fn cube(&self) -> Self {
self.square() * self
}
/// Doubles this element.
#[must_use]
fn double(&self) -> Self {
*self + *self
}
/// Computes the multiplicative inverse of this element,
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
unimplemented!()
}
/// Returns the square root of the field element, if it is
/// quadratic residue.
fn sqrt(&self) -> CtOption<Self> {
unimplemented!()
}
/// Computes:
///
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
/// field;
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
/// a nonsquare in the field;
///
/// where $G_S$ is a non-square.
///
/// # Warnings
///
/// - The choice of root from `sqrt` is unspecified.
/// - The value of $G_S$ is unspecified, and cannot be assumed to have any specific
/// value in a generic context.
fn sqrt_ratio(_: &Self, _: &Self) -> (Choice, Self) {
unimplemented!()
}
}
impl ConditionallySelectable for GoldilocksExt2 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self([
Goldilocks::conditional_select(&a.0[0], &b.0[0], choice),
Goldilocks::conditional_select(&a.0[1], &b.0[1], choice),
])
}
}
impl ConstantTimeEq for GoldilocksExt2 {
fn ct_eq(&self, other: &Self) -> Choice {
self.to_canonical_u64_array()
.ct_eq(&other.to_canonical_u64_array())
}
}
impl Neg for GoldilocksExt2 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self([-self.0[0], -self.0[1]])
}
}
impl Add for GoldilocksExt2 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]])
}
}
impl<'a> Add<&'a GoldilocksExt2> for GoldilocksExt2 {
type Output = Self;
#[inline]
fn add(self, rhs: &'a GoldilocksExt2) -> Self::Output {
self + *rhs
}
}
impl AddAssign for GoldilocksExt2 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<'a> AddAssign<&'a GoldilocksExt2> for GoldilocksExt2 {
#[inline]
fn add_assign(&mut self, rhs: &'a GoldilocksExt2) {
*self = *self + *rhs;
}
}
impl Sub for GoldilocksExt2 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]])
}
}
impl<'a> Sub<&'a GoldilocksExt2> for GoldilocksExt2 {
type Output = Self;
#[inline]
fn sub(self, rhs: &'a GoldilocksExt2) -> Self::Output {
self - *rhs
}
}
impl SubAssign for GoldilocksExt2 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<'a> SubAssign<&'a GoldilocksExt2> for GoldilocksExt2 {
#[inline]
fn sub_assign(&mut self, rhs: &'a GoldilocksExt2) {
*self = *self - *rhs;
}
}
impl<T: ::core::borrow::Borrow<GoldilocksExt2>> Sum<T> for GoldilocksExt2 {
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, item| acc + item.borrow())
}
}
impl Mul for GoldilocksExt2 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
mul_internal(&self, &rhs)
}
}
impl<'a> Mul<&'a GoldilocksExt2> for GoldilocksExt2 {
type Output = Self;
#[inline]
fn mul(self, rhs: &'a GoldilocksExt2) -> Self::Output {
self * *rhs
}
}
impl MulAssign for GoldilocksExt2 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<'a> MulAssign<&'a GoldilocksExt2> for GoldilocksExt2 {
#[inline]
fn mul_assign(&mut self, rhs: &'a GoldilocksExt2) {
*self = *self * *rhs;
}
}
impl<T: ::core::borrow::Borrow<GoldilocksExt2>> Product<T> for GoldilocksExt2 {
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, item| acc * item.borrow())
}
}
impl AsMut<[u8]> for GoldilocksExt2 {
fn as_mut(&mut self) -> &mut [u8] {
let ptr = self as *mut Self as *mut u8;
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
// with alignment greater than that of u8
unsafe { std::slice::from_raw_parts_mut(ptr, 16) }
}
}
impl AsRef<[u8]> for GoldilocksExt2 {
fn as_ref(&self) -> &[u8] {
let ptr = self as *const Self as *const u8;
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
// with alignment greater than that of u8
unsafe { std::slice::from_raw_parts(ptr, 16) }
}
}
impl From<Goldilocks> for GoldilocksExt2 {
fn from(a: Goldilocks) -> Self {
Self([a, Goldilocks::ZERO])
}
}
impl From<u64> for GoldilocksExt2 {
fn from(a: u64) -> Self {
Goldilocks::from(a).into()
}
}
/// This represents an element of a prime field.
impl PrimeField for GoldilocksExt2 {
/// The prime field can be converted back and forth into this binary
/// representation.
type Repr = Self;
/// Modulus of the field written as a string for debugging purposes.
///
/// The encoding of the modulus is implementation-specific. Generic users of the
/// `PrimeField` trait should treat this string as opaque.
const MODULUS: &'static str = "0xffffffff00000001";
/// How many bits are needed to represent an element of this field.
const NUM_BITS: u32 = 64;
/// How many bits of information can be reliably stored in the field element.
///
/// This is usually `Self::NUM_BITS - 1`.
const CAPACITY: u32 = 63;
/// An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd.
///
/// This is the number of leading zero bits in the little-endian bit representation of
/// `modulus - 1`.
const S: u32 = 32;
/// Inverse of $2$ in the field.
const TWO_INV: Self = GoldilocksExt2([Goldilocks::TWO_INV, Goldilocks::ZERO]);
/// A fixed multiplicative generator of `modulus - 1` order. This element must also be
/// a quadratic nonresidue.
///
/// It can be calculated using [SageMath] as `GF(modulus).primitive_element()`.
///
/// Implementations of this trait MUST ensure that this is the generator used to
/// derive `Self::ROOT_OF_UNITY`.
///
/// [SageMath]: https://www.sagemath.org/
const MULTIPLICATIVE_GENERATOR: Self =
GoldilocksExt2([Goldilocks::MULTIPLICATIVE_GENERATOR, Goldilocks::ZERO]);
/// The `2^s` root of unity.
///
/// It can be calculated by exponentiating `Self::MULTIPLICATIVE_GENERATOR` by `t`,
/// where `t = (modulus - 1) >> Self::S`.
const ROOT_OF_UNITY: Self = GoldilocksExt2([Goldilocks::ROOT_OF_UNITY, Goldilocks::ZERO]);
/// Inverse of [`Self::ROOT_OF_UNITY`].
const ROOT_OF_UNITY_INV: Self =
GoldilocksExt2([Goldilocks::ROOT_OF_UNITY_INV, Goldilocks::ZERO]);
/// Generator of the `t-order` multiplicative subgroup.
///
/// It can be calculated by exponentiating [`Self::MULTIPLICATIVE_GENERATOR`] by `2^s`,
/// where `s` is [`Self::S`].
const DELTA: Self = GoldilocksExt2([Goldilocks::DELTA, Goldilocks::ZERO]);
/// Attempts to convert a byte representation of a field element into an element of
/// this prime field, failing if the input is not canonical (is not smaller than the
/// field's modulus).
///
/// The byte representation is interpreted with the same endianness as elements
/// returned by [`PrimeField::to_repr`].
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
CtOption::new(repr, Choice::from(1))
}
/// Attempts to convert a byte representation of a field element into an element of
/// this prime field, failing if the input is not canonical (is not smaller than the
/// field's modulus).
///
/// The byte representation is interpreted with the same endianness as elements
/// returned by [`PrimeField::to_repr`].
///
/// # Security
///
/// This method provides **no** constant-time guarantees. Implementors of the
/// `PrimeField` trait **may** optimise this method using non-constant-time logic.
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
Self::from_repr(repr).into()
}
/// Converts an element of the prime field into the standard byte representation for
/// this field.
///
/// The endianness of the byte representation is implementation-specific. Generic
/// encodings of field elements should be treated as opaque.
fn to_repr(&self) -> Self::Repr {
*self
}
/// Returns true iff this element is odd.
fn is_odd(&self) -> Choice {
unimplemented!()
}
}

396
src/fp3.rs Normal file
View File

@@ -0,0 +1,396 @@
//! This module implements Goldilocks cubic extension field mod x^3-x-1
use crate::Goldilocks;
use core::iter::{Product, Sum};
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use ff::{Field, PrimeField};
use rand_core::RngCore;
use serde::{Deserialize, Serialize};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
/// Degree 3 Goldilocks extension field mod x^3-x-1
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GoldilocksExt3(pub [Goldilocks; 3]);
/// For a = (a1, a2, a3) and b = (b1, b2, b3)
/// The multiplication is define as
/// c := a * b = a(x) * b(x) % (x^3-x-1)
/// = x^2*a3*b1 + x^2*a2*b2 + x^2*a1*b3 + x^2*a3*b3
/// + x*a2*b1 + x*a1*b2 + x*a3*b2 + x*a2*b3 + x*a3*b3
/// + a1*b1 + a3*b2 + a2*b3
/// This requires 9 multiplications and 6 1 additions
fn mul_internal(a: &GoldilocksExt3, b: &GoldilocksExt3) -> GoldilocksExt3 {
// todo: optimizations?
let a1b1 = a.0[0] * b.0[0];
let a1b2 = a.0[0] * b.0[1];
let a1b3 = a.0[0] * b.0[2];
let a2b1 = a.0[1] * b.0[0];
let a2b2 = a.0[1] * b.0[1];
let a2b3 = a.0[1] * b.0[2];
let a3b1 = a.0[2] * b.0[0];
let a3b2 = a.0[2] * b.0[1];
let a3b3 = a.0[2] * b.0[2];
let c1 = a1b1 + a3b2 + a2b3;
let c2 = a2b1 + a1b2 + a2b3 + a3b2 + a3b3;
let c3 = a3b1 + a2b2 + a1b3 + a3b3;
GoldilocksExt3([c1, c2, c3])
}
impl GoldilocksExt3 {
fn to_canonical_u64_array(&self) -> [u64; 3] {
[
self.0[0].to_canonical_u64(),
self.0[1].to_canonical_u64(),
self.0[2].to_canonical_u64(),
]
}
}
impl Field for GoldilocksExt3 {
/// The zero element of the field, the additive identity.
const ZERO: Self = Self([Goldilocks::ZERO; 3]);
/// The one element of the field, the multiplicative identity.
const ONE: Self = Self([Goldilocks::ONE, Goldilocks::ZERO, Goldilocks::ZERO]);
/// Returns an element chosen uniformly at random using a user-provided RNG.
/// Note: this sampler is not constant time!
fn random(mut rng: impl RngCore) -> Self {
let a1 = Goldilocks::random(&mut rng);
let a2 = Goldilocks::random(&mut rng);
let a3 = Goldilocks::random(&mut rng);
Self([a1, a2, a3])
}
/// Squares this element.
#[must_use]
fn square(&self) -> Self {
*self * *self
}
/// Cubes this element.
#[must_use]
fn cube(&self) -> Self {
self.square() * self
}
/// Doubles this element.
#[must_use]
fn double(&self) -> Self {
*self + *self
}
/// Computes the multiplicative inverse of this element,
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
unimplemented!()
}
/// Returns the square root of the field element, if it is
/// quadratic residue.
fn sqrt(&self) -> CtOption<Self> {
unimplemented!()
}
/// Computes:
///
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
/// field;
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
/// a nonsquare in the field;
///
/// where $G_S$ is a non-square.
///
/// # Warnings
///
/// - The choice of root from `sqrt` is unspecified.
/// - The value of $G_S$ is unspecified, and cannot be assumed to have any specific
/// value in a generic context.
fn sqrt_ratio(_: &Self, _: &Self) -> (Choice, Self) {
unimplemented!()
}
}
impl ConditionallySelectable for GoldilocksExt3 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self([
Goldilocks::conditional_select(&a.0[0], &b.0[0], choice),
Goldilocks::conditional_select(&a.0[1], &b.0[1], choice),
Goldilocks::conditional_select(&a.0[2], &b.0[2], choice),
])
}
}
impl ConstantTimeEq for GoldilocksExt3 {
fn ct_eq(&self, other: &Self) -> Choice {
self.to_canonical_u64_array()
.ct_eq(&other.to_canonical_u64_array())
}
}
impl Neg for GoldilocksExt3 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self([-self.0[0], -self.0[1], -self.0[2]])
}
}
impl Add for GoldilocksExt3 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self([
self.0[0] + rhs.0[0],
self.0[1] + rhs.0[1],
self.0[2] + rhs.0[2],
])
}
}
impl<'a> Add<&'a GoldilocksExt3> for GoldilocksExt3 {
type Output = Self;
#[inline]
fn add(self, rhs: &'a GoldilocksExt3) -> Self::Output {
self + *rhs
}
}
impl AddAssign for GoldilocksExt3 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<'a> AddAssign<&'a GoldilocksExt3> for GoldilocksExt3 {
#[inline]
fn add_assign(&mut self, rhs: &'a GoldilocksExt3) {
*self = *self + *rhs;
}
}
impl Sub for GoldilocksExt3 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self([
self.0[0] - rhs.0[0],
self.0[1] - rhs.0[1],
self.0[2] - rhs.0[2],
])
}
}
impl<'a> Sub<&'a GoldilocksExt3> for GoldilocksExt3 {
type Output = Self;
#[inline]
fn sub(self, rhs: &'a GoldilocksExt3) -> Self::Output {
self - *rhs
}
}
impl SubAssign for GoldilocksExt3 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<'a> SubAssign<&'a GoldilocksExt3> for GoldilocksExt3 {
#[inline]
fn sub_assign(&mut self, rhs: &'a GoldilocksExt3) {
*self = *self - *rhs;
}
}
impl<T: ::core::borrow::Borrow<GoldilocksExt3>> Sum<T> for GoldilocksExt3 {
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, item| acc + item.borrow())
}
}
impl Mul for GoldilocksExt3 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
mul_internal(&self, &rhs)
}
}
impl<'a> Mul<&'a GoldilocksExt3> for GoldilocksExt3 {
type Output = Self;
#[inline]
fn mul(self, rhs: &'a GoldilocksExt3) -> Self::Output {
self * *rhs
}
}
impl MulAssign for GoldilocksExt3 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<'a> MulAssign<&'a GoldilocksExt3> for GoldilocksExt3 {
#[inline]
fn mul_assign(&mut self, rhs: &'a GoldilocksExt3) {
*self = *self * *rhs;
}
}
impl<T: ::core::borrow::Borrow<GoldilocksExt3>> Product<T> for GoldilocksExt3 {
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, item| acc * item.borrow())
}
}
impl AsMut<[u8]> for GoldilocksExt3 {
fn as_mut(&mut self) -> &mut [u8] {
let ptr = self as *mut Self as *mut u8;
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
// with alignment greater than that of u8
unsafe { std::slice::from_raw_parts_mut(ptr, 24) }
}
}
impl AsRef<[u8]> for GoldilocksExt3 {
fn as_ref(&self) -> &[u8] {
let ptr = self as *const Self as *const u8;
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
// with alignment greater than that of u8
unsafe { std::slice::from_raw_parts(ptr, 24) }
}
}
impl From<Goldilocks> for GoldilocksExt3 {
fn from(a: Goldilocks) -> Self {
Self([a, Goldilocks::ZERO, Goldilocks::ZERO])
}
}
impl From<u64> for GoldilocksExt3 {
fn from(a: u64) -> Self {
Goldilocks::from(a).into()
}
}
/// This represents an element of a prime field.
impl PrimeField for GoldilocksExt3 {
/// The prime field can be converted back and forth into this binary
/// representation.
type Repr = Self;
/// Modulus of the field written as a string for debugging purposes.
///
/// The encoding of the modulus is implementation-specific. Generic users of the
/// `PrimeField` trait should treat this string as opaque.
const MODULUS: &'static str = "0xffffffff00000001";
/// How many bits are needed to represent an element of this field.
const NUM_BITS: u32 = 64;
/// How many bits of information can be reliably stored in the field element.
///
/// This is usually `Self::NUM_BITS - 1`.
const CAPACITY: u32 = 63;
/// An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd.
///
/// This is the number of leading zero bits in the little-endian bit representation of
/// `modulus - 1`.
const S: u32 = 32;
/// Inverse of $2$ in the field.
const TWO_INV: Self = GoldilocksExt3([Goldilocks::TWO_INV, Goldilocks::ZERO, Goldilocks::ZERO]);
/// A fixed multiplicative generator of `modulus - 1` order. This element must also be
/// a quadratic nonresidue.
///
/// It can be calculated using [SageMath] as `GF(modulus).primitive_element()`.
///
/// Implementations of this trait MUST ensure that this is the generator used to
/// derive `Self::ROOT_OF_UNITY`.
///
/// [SageMath]: https://www.sagemath.org/
const MULTIPLICATIVE_GENERATOR: Self = GoldilocksExt3([
Goldilocks::MULTIPLICATIVE_GENERATOR,
Goldilocks::ZERO,
Goldilocks::ZERO,
]);
/// The `2^s` root of unity.
///
/// It can be calculated by exponentiating `Self::MULTIPLICATIVE_GENERATOR` by `t`,
/// where `t = (modulus - 1) >> Self::S`.
const ROOT_OF_UNITY: Self = GoldilocksExt3([
Goldilocks::ROOT_OF_UNITY,
Goldilocks::ZERO,
Goldilocks::ZERO,
]);
/// Inverse of [`Self::ROOT_OF_UNITY`].
const ROOT_OF_UNITY_INV: Self = GoldilocksExt3([
Goldilocks::ROOT_OF_UNITY_INV,
Goldilocks::ZERO,
Goldilocks::ZERO,
]);
/// Generator of the `t-order` multiplicative subgroup.
///
/// It can be calculated by exponentiating [`Self::MULTIPLICATIVE_GENERATOR`] by `2^s`,
/// where `s` is [`Self::S`].
const DELTA: Self = GoldilocksExt3([Goldilocks::DELTA, Goldilocks::ZERO, Goldilocks::ZERO]);
/// Attempts to convert a byte representation of a field element into an element of
/// this prime field, failing if the input is not canonical (is not smaller than the
/// field's modulus).
///
/// The byte representation is interpreted with the same endianness as elements
/// returned by [`PrimeField::to_repr`].
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
CtOption::new(repr, Choice::from(1))
}
/// Attempts to convert a byte representation of a field element into an element of
/// this prime field, failing if the input is not canonical (is not smaller than the
/// field's modulus).
///
/// The byte representation is interpreted with the same endianness as elements
/// returned by [`PrimeField::to_repr`].
///
/// # Security
///
/// This method provides **no** constant-time guarantees. Implementors of the
/// `PrimeField` trait **may** optimise this method using non-constant-time logic.
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
Self::from_repr(repr).into()
}
/// Converts an element of the prime field into the standard byte representation for
/// this field.
///
/// The endianness of the byte representation is implementation-specific. Generic
/// encodings of field elements should be treated as opaque.
fn to_repr(&self) -> Self::Repr {
*self
}
/// Returns true iff this element is odd.
fn is_odd(&self) -> Choice {
unimplemented!()
}
}

16
src/lib.rs Normal file
View File

@@ -0,0 +1,16 @@
//! This crate implements Goldilocks field with modulus 2^64 - 2^32 + 1
//! Credit: the majority of the code is borrowed or inspired from Plonky2 with modifications.
pub use field::SmallField;
pub use fp::Goldilocks;
pub use fp2::GoldilocksExt2;
pub use fp3::GoldilocksExt3;
mod field;
mod fp;
mod fp2;
mod fp3;
mod util;
#[cfg(test)]
mod tests;

241
src/tests.rs Normal file
View File

@@ -0,0 +1,241 @@
mod fp;
mod fp2;
mod fp3;
use ark_std::{end_timer, start_timer};
use ff::Field;
use ff::PrimeField;
use rand_core::RngCore;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
pub fn random_field_tests<F: Field>(type_name: String) {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
random_multiplication_tests::<F, _>(&mut rng, type_name.clone());
random_addition_tests::<F, _>(&mut rng, type_name.clone());
random_subtraction_tests::<F, _>(&mut rng, type_name.clone());
random_negation_tests::<F, _>(&mut rng, type_name.clone());
random_doubling_tests::<F, _>(&mut rng, type_name.clone());
random_squaring_tests::<F, _>(&mut rng, type_name.clone());
// random_inversion_tests::<F, _>(&mut rng, type_name.clone());
random_expansion_tests::<F, _>(&mut rng, type_name);
assert_eq!(F::ZERO.is_zero().unwrap_u8(), 1);
{
let mut z = F::ZERO;
z = z.neg();
assert_eq!(z.is_zero().unwrap_u8(), 1);
}
// assert!(bool::from(F::ZERO.invert().is_none()));
// Multiplication by zero
{
let mut a = F::random(&mut rng);
a.mul_assign(&F::ZERO);
assert_eq!(a.is_zero().unwrap_u8(), 1);
}
// Addition by zero
{
let mut a = F::random(&mut rng);
let copy = a;
a.add_assign(&F::ZERO);
assert_eq!(a, copy);
}
}
fn random_multiplication_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("multiplication {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let c = F::random(&mut rng);
let mut t0 = a; // (a * b) * c
t0.mul_assign(&b);
t0.mul_assign(&c);
let mut t1 = a; // (a * c) * b
t1.mul_assign(&c);
t1.mul_assign(&b);
let mut t2 = b; // (b * c) * a
t2.mul_assign(&c);
t2.mul_assign(&a);
assert_eq!(t0, t1);
assert_eq!(t1, t2);
}
end_timer!(start);
}
fn random_addition_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("addition {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let c = F::random(&mut rng);
let mut t0 = a; // (a + b) + c
t0.add_assign(&b);
t0.add_assign(&c);
let mut t1 = a; // (a + c) + b
t1.add_assign(&c);
t1.add_assign(&b);
let mut t2 = b; // (b + c) + a
t2.add_assign(&c);
t2.add_assign(&a);
assert_eq!(t0, t1);
assert_eq!(t1, t2);
}
end_timer!(start);
}
fn random_subtraction_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("subtraction {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let mut t0 = a; // (a - b)
t0.sub_assign(&b);
let mut t1 = b; // (b - a)
t1.sub_assign(&a);
let mut t2 = t0; // (a - b) + (b - a) = 0
t2.add_assign(&t1);
assert_eq!(t2.is_zero().unwrap_u8(), 1);
}
end_timer!(start);
}
fn random_negation_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("negation {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let mut b = a;
b = b.neg();
b.add_assign(&a);
assert_eq!(b.is_zero().unwrap_u8(), 1);
}
end_timer!(start);
}
fn random_doubling_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("doubling {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let mut a = F::random(&mut rng);
let mut b = a;
a.add_assign(&b);
b = b.double();
assert_eq!(a, b);
}
end_timer!(start);
}
fn random_squaring_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("squaring {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let mut a = F::random(&mut rng);
let mut b = a;
a.mul_assign(&b);
b = b.square();
assert_eq!(a, b);
}
end_timer!(start);
}
#[allow(dead_code)]
fn random_inversion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
assert!(bool::from(F::ZERO.invert().is_none()));
let message = format!("inversion {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let mut a = F::random(&mut rng);
let b = a.invert().unwrap(); // probabilistically nonzero
a.mul_assign(&b);
assert_eq!(a, F::ONE);
}
end_timer!(start);
}
fn random_expansion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("expansion {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let c = F::random(&mut rng);
let d = F::random(&mut rng);
let mut t0 = a;
t0.add_assign(&b);
let mut t1 = c;
t1.add_assign(&d);
t0.mul_assign(&t1);
let mut t2 = a;
t2.mul_assign(&c);
let mut t3 = b;
t3.mul_assign(&c);
let mut t4 = a;
t4.mul_assign(&d);
let mut t5 = b;
t5.mul_assign(&d);
t2.add_assign(&t3);
t2.add_assign(&t4);
t2.add_assign(&t5);
assert_eq!(t0, t2);
}
end_timer!(start);
}
pub fn random_prime_field_tests<F: PrimeField>(type_name: String) {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
random_serdes_tests::<F, _>(&mut rng, type_name);
}
fn random_serdes_tests<F: PrimeField, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("expansion {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
// convert a into and from repr
let a = F::random(&mut rng);
let b = a.to_repr();
let c = F::from_repr(b).unwrap();
let d = c.to_repr();
assert_eq!(a, c);
assert_eq!(b.as_ref(), d.as_ref());
}
end_timer!(start);
}

57
src/tests/fp.rs Normal file
View File

@@ -0,0 +1,57 @@
use std::ops::Neg;
use ff::Field;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use super::random_field_tests;
use super::random_prime_field_tests;
use crate::fp::Goldilocks;
use crate::fp::LegendreSymbol;
#[test]
fn test_sqrt_fq() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let two_inv = Goldilocks(0x7fffffff80000001);
let v = two_inv.square().sqrt().unwrap();
assert!(v == two_inv || (-v) == two_inv);
for _ in 0..10000 {
let a = Goldilocks::random(&mut rng);
let mut b = a;
b = b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
let b = b.sqrt().unwrap();
let mut negb = b;
negb = negb.neg();
assert!(a == b || a == negb);
}
let mut c = Goldilocks::ONE;
for _ in 0..10000 {
let mut b = c;
b = b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
b = b.sqrt().unwrap();
if b != c {
b = b.neg();
}
assert_eq!(b, c);
c += &Goldilocks::ONE;
}
}
#[test]
fn test_field() {
random_field_tests::<Goldilocks>("Goldilocks".to_string());
random_prime_field_tests::<Goldilocks>("Goldilocks".to_string());
}

18
src/tests/fp2.rs Normal file
View File

@@ -0,0 +1,18 @@
use super::random_field_tests;
use super::random_prime_field_tests;
use crate::fp::Goldilocks;
use crate::fp2::GoldilocksExt2;
#[test]
fn test_field() {
random_field_tests::<GoldilocksExt2>("GoldilocksExt3".to_string());
random_prime_field_tests::<GoldilocksExt2>("GoldilocksExt3".to_string());
}
#[test]
fn known_answer_tests() {
let a = GoldilocksExt2([Goldilocks::from(1), Goldilocks::from(2)]);
let b = GoldilocksExt2([Goldilocks::from(3), Goldilocks::from(4)]);
let c = GoldilocksExt2([-Goldilocks::from(5), Goldilocks::from(10)]);
assert_eq!(a * b, c)
}

30
src/tests/fp3.rs Normal file
View File

@@ -0,0 +1,30 @@
use super::random_field_tests;
use super::random_prime_field_tests;
use crate::fp::Goldilocks;
use crate::fp3::GoldilocksExt3;
#[test]
fn test_field() {
random_field_tests::<GoldilocksExt3>("GoldilocksExt3".to_string());
random_prime_field_tests::<GoldilocksExt3>("GoldilocksExt3".to_string());
}
#[test]
fn known_answer_tests() {
let a = GoldilocksExt3([
Goldilocks::from(1),
Goldilocks::from(2),
Goldilocks::from(3),
]);
let b = GoldilocksExt3([
Goldilocks::from(4),
Goldilocks::from(5),
Goldilocks::from(6),
]);
let c = GoldilocksExt3([
Goldilocks::from(31),
Goldilocks::from(58),
Goldilocks::from(46),
]);
assert_eq!(a * b, c)
}

292
src/util.rs Normal file
View File

@@ -0,0 +1,292 @@
use std::arch::asm;
use std::hint::unreachable_unchecked;
use ff::{Field, PrimeField};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::fp::{Goldilocks, MODULUS};
#[inline(always)]
pub fn assume(p: bool) {
debug_assert!(p);
if !p {
unsafe {
unreachable_unchecked();
}
}
}
/// Try to force Rust to emit a branch. Example:
/// if x > 2 {
/// y = foo();
/// branch_hint();
/// } else {
/// y = bar();
/// }
/// This function has no semantics. It is a hint only.
#[inline(always)]
pub fn branch_hint() {
unsafe {
asm!("", options(nomem, nostack, preserves_flags));
}
}
/// Fast addition modulo ORDER for x86-64.
/// This function is marked unsafe for the following reasons:
/// - It is only correct if x + y < 2**64 + ORDER = 0x1ffffffff00000001.
/// - It is only faster in some circumstances. In particular, on x86 it overwrites both inputs in
/// the registers, so its use is not recommended when either input will be used again.
#[inline(always)]
#[cfg(target_arch = "x86_64")]
pub(crate) unsafe fn add_no_canonicalize_trashing_input(x: u64, y: u64) -> u64 {
let res_wrapped: u64;
let adjustment: u64;
asm!(
"add {0}, {1}",
// Trick. The carry flag is set iff the addition overflowed.
// sbb x, y does x := x - y - CF. In our case, x and y are both {1:e}, so it simply does
// {1:e} := 0xffffffff on overflow and {1:e} := 0 otherwise. {1:e} is the low 32 bits of
// {1}; the high 32-bits are zeroed on write. In the end, we end up with 0xffffffff in {1}
// on overflow; this happens be EPSILON.
// Note that the CPU does not realize that the result of sbb x, x does not actually depend
// on x. We must write the result to a register that we know to be ready. We have a
// dependency on {1} anyway, so let's use it.
"sbb {1:e}, {1:e}",
inlateout(reg) x => res_wrapped,
inlateout(reg) y => adjustment,
options(pure, nomem, nostack),
);
assume(x != 0 || (res_wrapped == y && adjustment == 0));
assume(y != 0 || (res_wrapped == x && adjustment == 0));
// Add EPSILON == subtract ORDER.
// Cannot overflow unless the assumption if x + y < 2**64 + ORDER is incorrect.
res_wrapped + adjustment
}
#[inline(always)]
#[cfg(not(target_arch = "x86_64"))]
pub(crate) unsafe fn add_no_canonicalize_trashing_input(x: u64, y: u64) -> u64 {
let (res_wrapped, carry) = x.overflowing_add(y);
// Below cannot overflow unless the assumption if x + y < 2**64 + ORDER is incorrect.
res_wrapped + EPSILON * (carry as u64)
}
#[inline]
pub(crate) fn split(x: u128) -> (u64, u64) {
(x as u64, (x >> 64) as u64)
}
/// Try to invert an element in a prime field.
///
/// The algorithm below is the "plus-minus-inversion" method
/// with an "almost Montgomery inverse" flair. See Handbook of
/// Elliptic and Hyperelliptic Cryptography, Algorithms 11.6
/// and 11.12.
#[allow(clippy::many_single_char_names)]
pub(crate) fn try_inverse_u64(x: &u64) -> Option<u64> {
let mut f = *x;
let mut g = MODULUS;
// NB: These two are very rarely such that their absolute
// value exceeds (p-1)/2; we are paying the price of i128 for
// the whole calculation, just for the times they do
// though. Measurements suggest a further 10% time saving if c
// and d could be replaced with i64's.
let mut c = 1i128;
let mut d = 0i128;
if f == 0 {
return None;
}
// f and g must always be odd.
let mut k = f.trailing_zeros();
f >>= k;
if f == 1 {
return Some(inverse_2exp(k as usize));
}
// The first two iterations are unrolled. This is to handle
// the case where f and g are both large and f+g can
// overflow. log2(max{f,g}) goes down by at least one each
// iteration though, so after two iterations we can be sure
// that f+g won't overflow.
// Iteration 1:
safe_iteration(&mut f, &mut g, &mut c, &mut d, &mut k);
if f == 1 {
// c must be -1 or 1 here.
if c == -1 {
return Some(MODULUS - inverse_2exp(k as usize));
}
debug_assert!(c == 1, "bug in try_inverse_u64");
return Some(inverse_2exp(k as usize));
}
// Iteration 2:
safe_iteration(&mut f, &mut g, &mut c, &mut d, &mut k);
// Remaining iterations:
while f != 1 {
unsafe {
unsafe_iteration(&mut f, &mut g, &mut c, &mut d, &mut k);
}
}
// The following two loops adjust c so it's in the canonical range
// [0, F::ORDER).
// The maximum number of iterations observed here is 2; should
// prove this.
while c < 0 {
c += MODULUS as i128;
}
// The maximum number of iterations observed here is 1; should
// prove this.
while c >= MODULUS as i128 {
c -= MODULUS as i128;
}
// Precomputing the binary inverses rather than using inverse_2exp
// saves ~5ns on my machine.
let res = Goldilocks(c as u64) * Goldilocks(inverse_2exp(k as usize));
debug_assert!(
Goldilocks(*x) * res == Goldilocks::ONE,
"bug in try_inverse_u64"
);
Some(res.0)
}
/// This is a 'safe' iteration for the modular inversion algorithm. It
/// is safe in the sense that it will produce the right answer even
/// when f + g >= 2^64.
#[inline(always)]
fn safe_iteration(f: &mut u64, g: &mut u64, c: &mut i128, d: &mut i128, k: &mut u32) {
if f < g {
std::mem::swap(f, g);
std::mem::swap(c, d);
}
if *f & 3 == *g & 3 {
// f - g = 0 (mod 4)
*f -= *g;
*c -= *d;
// kk >= 2 because f is now 0 (mod 4).
let kk = f.trailing_zeros();
*f >>= kk;
*d <<= kk;
*k += kk;
} else {
// f + g = 0 (mod 4)
*f = (*f >> 2) + (*g >> 2) + 1u64;
*c += *d;
let kk = f.trailing_zeros();
*f >>= kk;
*d <<= kk + 2;
*k += kk + 2;
}
}
/// This is an 'unsafe' iteration for the modular inversion
/// algorithm. It is unsafe in the sense that it might produce the
/// wrong answer if f + g >= 2^64.
#[inline(always)]
unsafe fn unsafe_iteration(f: &mut u64, g: &mut u64, c: &mut i128, d: &mut i128, k: &mut u32) {
if *f < *g {
std::mem::swap(f, g);
std::mem::swap(c, d);
}
if *f & 3 == *g & 3 {
// f - g = 0 (mod 4)
*f -= *g;
*c -= *d;
} else {
// f + g = 0 (mod 4)
*f += *g;
*c += *d;
}
// kk >= 2 because f is now 0 (mod 4).
let kk = f.trailing_zeros();
*f >>= kk;
*d <<= kk;
*k += kk;
}
/// Compute the inverse of 2^exp in this field.
#[inline]
fn inverse_2exp(exp: usize) -> u64 {
// Let p = char(F). Since 2^exp is in the prime subfield, i.e. an
// element of GF_p, its inverse must be as well. Thus we may add
// multiples of p without changing the result. In particular,
// 2^-exp = 2^-exp - p 2^-exp
// = 2^-exp (1 - p)
// = p - (p - 1) / 2^exp
// If this field's two adicity, t, is at least exp, then 2^exp divides
// p - 1, so this division can be done with a simple bit shift. If
// exp > t, we repeatedly multiply by 2^-t and reduce exp until it's in
// the right range.
// NB: The only reason this is split into two cases is to save
// the multiplication (and possible calculation of
// inverse_2_pow_adicity) in the usual case that exp <=
// TWO_ADICITY. Can remove the branch and simplify if that
// saving isn't worth it.
let res = if exp > 32 {
// NB: This should be a compile-time constant
// MODULUS - ((MODULUS - 1) >> 32)
let inverse_2_pow_adicity = Goldilocks(0xfffffffe00000002);
let mut res = inverse_2_pow_adicity;
let mut e = exp - 32;
while e > 32 {
res *= inverse_2_pow_adicity;
e -= 32;
}
res * Goldilocks(MODULUS - ((MODULUS - 1) >> e))
} else {
Goldilocks(MODULUS - ((MODULUS - 1) >> exp))
};
res.0
}
pub(crate) fn sqrt_tonelli_shanks(f: &Goldilocks, tm1d2: u64) -> CtOption<Goldilocks> {
// w = self^((t - 1) // 2)
let w = f.pow_vartime([tm1d2]);
let mut v = Goldilocks::S;
let mut x = w * f;
let mut b = x * w;
// Initialize z as the 2^S root of unity.
let mut z = Goldilocks::ROOT_OF_UNITY;
for max_v in (1..=Goldilocks::S).rev() {
let mut k = 1;
let mut tmp = b.square();
let mut j_less_than_v: Choice = 1.into();
for j in 2..max_v {
let tmp_is_one = tmp.ct_eq(&Goldilocks::ONE);
let squared = Goldilocks::conditional_select(&tmp, &z, tmp_is_one).square();
tmp = Goldilocks::conditional_select(&squared, &tmp, tmp_is_one);
let new_z = Goldilocks::conditional_select(&z, &squared, tmp_is_one);
j_less_than_v &= !j.ct_eq(&v);
k = u32::conditional_select(&j, &k, tmp_is_one);
z = Goldilocks::conditional_select(&z, &new_z, j_less_than_v);
}
let result = x * z;
x = Goldilocks::conditional_select(&result, &x, b.ct_eq(&Goldilocks::ONE));
z = z.square();
b *= z;
v = k;
}
CtOption::new(
x,
(x * x).ct_eq(f), // Only return Some if it's the square root.
)
}