From b55f9418316cda3ab1bee886fe5267b35e751bb9 Mon Sep 17 00:00:00 2001 From: dark64 Date: Tue, 18 Feb 2020 17:49:14 +0100 Subject: [PATCH] g16 native verifier --- Cargo.lock | 7 + zokrates_cli/src/bin.rs | 66 ++++-- zokrates_core/Cargo.toml | 1 + zokrates_core/src/proof_system/bn128/g16.rs | 213 +++++++++++------- .../src/proof_system/bn128/utils/bellman.rs | 55 ++--- .../src/proof_system/bn128/utils/mod.rs | 1 - .../src/proof_system/bn128/utils/parser.rs | 48 ---- zokrates_core/src/proof_system/mod.rs | 10 +- zokrates_field/src/field.rs | 10 +- 9 files changed, 208 insertions(+), 203 deletions(-) delete mode 100644 zokrates_core/src/proof_system/bn128/utils/parser.rs diff --git a/Cargo.lock b/Cargo.lock index 199085f6..fca6905c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,12 @@ dependencies = [ "libc", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bellman_ce" version = "0.3.1" @@ -1737,6 +1743,7 @@ name = "zokrates_core" version = "0.4.1" dependencies = [ "assert_cli", + "base64", "bellman_ce 0.3.1 (git+https://github.com/matter-labs/bellman?rev=9e35737)", "bincode", "cc", diff --git a/zokrates_cli/src/bin.rs b/zokrates_cli/src/bin.rs index e6b10c9d..f0429d61 100644 --- a/zokrates_cli/src/bin.rs +++ b/zokrates_cli/src/bin.rs @@ -117,10 +117,10 @@ fn cli() -> Result<(), String> { ) .subcommand(SubCommand::with_name("export-verifier") .about("Exports a verifier as Solidity smart contract") - .arg(Arg::with_name("input") - .short("i") - .long("input") - .help("Path of the verifier") + .arg(Arg::with_name("verification-key-path") + .short("v") + .long("verification-key-path") + .help("Path of the generated verification key file") .value_name("FILE") .takes_value(true) .required(false) @@ -217,9 +217,9 @@ fn cli() -> Result<(), String> { .takes_value(true) .required(false) .default_value(PROVING_KEY_DEFAULT_PATH) - ).arg(Arg::with_name("proofpath") + ).arg(Arg::with_name("proof-path") .short("j") - .long("proofpath") + .long("proof-path") .help("Path of the JSON proof file") .value_name("FILE") .takes_value(true) @@ -245,9 +245,9 @@ fn cli() -> Result<(), String> { ) .subcommand(SubCommand::with_name("print-proof") .about("Prints proof in chosen format [remix, json]") - .arg(Arg::with_name("proofpath") + .arg(Arg::with_name("proof-path") .short("j") - .long("proofpath") + .long("proof-path") .help("Path of the JSON proof file") .value_name("FILE") .takes_value(true) @@ -265,9 +265,9 @@ fn cli() -> Result<(), String> { ) .subcommand(SubCommand::with_name("verify") .about("Verifies a given proof with the given constraint system and verification key") - .arg(Arg::with_name("proofpath") + .arg(Arg::with_name("proof-path") .short("j") - .long("proofpath") + .long("proof-path") .help("Path of the JSON proof file") .value_name("FILE") .takes_value(true) @@ -544,21 +544,21 @@ fn cli() -> Result<(), String> { { let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?; - let is_abiv2 = sub_matches.value_of("solidity-abi").unwrap() == "v2"; + let is_abi_v2 = sub_matches.value_of("solidity-abi").unwrap() == "v2"; println!("Exporting verifier..."); // read vk file - let input_path = Path::new(sub_matches.value_of("input").unwrap()); - let input_file = File::open(&input_path) - .map_err(|why| format!("couldn't open {}: {}", input_path.display(), why))?; - let mut reader = BufReader::new(input_file); + let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap()); + let vk_file = File::open(&vk_path) + .map_err(|why| format!("couldn't open {}: {}", vk_path.display(), why))?; - let mut vk = String::new(); + let mut reader = BufReader::new(vk_file); + let mut vk = Vec::new(); reader - .read_to_string(&mut vk) - .map_err(|why| format!("couldn't read {}: {}", input_path.display(), why))?; + .read_to_end(&mut vk) + .map_err(|why| format!("couldn't read {}: {}", vk_path.display(), why))?; - let verifier = scheme.export_solidity_verifier(vk, is_abiv2); + let verifier = scheme.export_solidity_verifier(vk, is_abi_v2); //write output file let output_path = Path::new(sub_matches.value_of("output").unwrap()); @@ -589,7 +589,7 @@ fn cli() -> Result<(), String> { .map_err(|why| format!("could not load witness: {:?}", why))?; let pk_path = Path::new(sub_matches.value_of("provingkey").unwrap()); - let proof_path = Path::new(sub_matches.value_of("proofpath").unwrap()); + let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap()); let program_path = Path::new(sub_matches.value_of("input").unwrap()); let program_file = File::open(&program_path) @@ -621,7 +621,7 @@ fn cli() -> Result<(), String> { ("print-proof", Some(sub_matches)) => { let format = sub_matches.value_of("format").unwrap(); - let path = Path::new(sub_matches.value_of("proofpath").unwrap()); + let path = Path::new(sub_matches.value_of("proof-path").unwrap()); let file = File::open(&path) .map_err(|why| format!("couldn't open {}: {}", path.display(), why))?; @@ -658,12 +658,28 @@ fn cli() -> Result<(), String> { ("verify", Some(sub_matches)) => { let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?; - let proof_path = sub_matches.value_of("proofpath").unwrap(); - let vk_path = sub_matches.value_of("verification-key-path").unwrap(); + let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap()); + let vk_file = File::open(&vk_path) + .map_err(|why| format!("couldn't open {}: {}", vk_path.display(), why))?; + + let mut vk_reader = BufReader::new(vk_file); + let mut vk = Vec::new(); + vk_reader + .read_to_end(&mut vk) + .map_err(|why| format!("couldn't read {}: {}", vk_path.display(), why))?; + + let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap()); + let proof_file = File::open(&proof_path) + .map_err(|why| format!("couldn't open {}: {}", proof_path.display(), why))?; + + let mut proof = String::new(); + let mut proof_reader = BufReader::new(proof_file); + proof_reader + .read_to_string(&mut proof) + .map_err(|why| format!("couldn't read {}: {}", proof_path.display(), why))?; println!("Performing verification..."); - - scheme.verify(String::from(vk_path), String::from(proof_path)); + println!("Verified: {}", scheme.verify(vk, proof)); } _ => unreachable!(), } diff --git a/zokrates_core/Cargo.toml b/zokrates_core/Cargo.toml index 2773e391..9f957f4f 100644 --- a/zokrates_core/Cargo.toml +++ b/zokrates_core/Cargo.toml @@ -24,6 +24,7 @@ serde_derive = "1.0" serde_json = "1.0" serde_bytes = "0.10" bincode = "0.8.0" +base64 = "0.11.0" regex = "0.2" pairing_ce = "0.18" ff_ce = "0.7" diff --git a/zokrates_core/src/proof_system/bn128/g16.rs b/zokrates_core/src/proof_system/bn128/g16.rs index e7d6cd39..fa4e5b33 100644 --- a/zokrates_core/src/proof_system/bn128/g16.rs +++ b/zokrates_core/src/proof_system/bn128/g16.rs @@ -1,17 +1,20 @@ -use std::io::{Cursor, Read}; +extern crate base64; -use bellman::groth16::Parameters; +use bellman::groth16::*; +use bellman::pairing::bn256::{Bn256, Fr}; use regex::Regex; -use zokrates_field::field::FieldPrime; +use zokrates_field::field::{Field, FieldPrime}; use crate::ir; -use crate::proof_system::{ProofSystem, SetupKeypair}; use crate::proof_system::bn128::utils::bellman::Computation; -use crate::proof_system::bn128::utils::parser::KeyValueParser; +use crate::proof_system::bn128::utils::bellman::{ + parse_fr, parse_g1, parse_g1_hex, parse_g2, parse_g2_hex, +}; use crate::proof_system::bn128::utils::solidity::{ SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB, SOLIDITY_PAIRING_LIB_V2, }; +use crate::proof_system::{ProofSystem, SetupKeypair}; const G16_WARNING: &str = "WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/reference/proving_schemes.html#g16-malleability for implications."; @@ -23,6 +26,35 @@ impl G16 { } } +type G1PairingPoint = (String, String); +type G2PairingPoint = (G1PairingPoint, G1PairingPoint); + +#[derive(Serialize, Deserialize)] +struct G16ProofPoints { + a: G1PairingPoint, + b: G2PairingPoint, + c: G1PairingPoint, +} + +#[derive(Serialize, Deserialize)] +struct G16Proof { + proof: G16ProofPoints, + inputs: Vec, + raw: String, +} + +impl G16ProofPoints { + fn new(a: G1PairingPoint, b: G2PairingPoint, c: G1PairingPoint) -> Self { + G16ProofPoints { a, b, c } + } +} + +impl G16Proof { + fn new(proof: G16ProofPoints, inputs: Vec, raw: String) -> Self { + G16Proof { proof, inputs, raw } + } +} + impl ProofSystem for G16 { fn setup(&self, program: ir::Prog) -> SetupKeypair { #[cfg(not(target_arch = "wasm32"))] @@ -30,16 +62,17 @@ impl ProofSystem for G16 { println!("{}", G16_WARNING); let parameters = Computation::without_witness(program).setup(); - let mut cursor = Cursor::new(Vec::new()); - parameters.write(&mut cursor).unwrap(); - cursor.set_position(0); - - let vk: String = serialize::serialize_vk(parameters.vk); let mut pk: Vec = Vec::new(); - cursor - .read_to_end(&mut pk) - .expect("Could not read cursor buffer"); + parameters + .write(&mut pk) + .expect("Could not write proving key to buffer"); + + let mut vk: Vec = Vec::new(); + parameters + .vk + .write(&mut vk) + .expect("Could not write verifying key to buffer"); SetupKeypair::from(vk, pk) } @@ -59,12 +92,30 @@ impl ProofSystem for G16 { let params = Parameters::read(proving_key.as_slice(), true).unwrap(); let proof = computation.clone().prove(¶ms); - serialize::serialize_proof(&proof, &computation.public_inputs_values()) + let mut raw: Vec = Vec::new(); + proof + .write(&mut raw) + .expect("Could not write proof to buffer"); + + let proof_points = + G16ProofPoints::new(parse_g1(&proof.a), parse_g2(&proof.b), parse_g1(&proof.c)); + + let g16_proof = G16Proof::new( + proof_points, + computation + .public_inputs_values() + .iter() + .map(parse_fr) + .collect::>(), + base64::encode(&raw), + ); + + format!("{:#}", serde_json::to_string(&g16_proof).unwrap()) } - fn export_solidity_verifier(&self, vk: String, is_abiv2: bool) -> String { - let vk_map = vk.parse_pairs(); - let (mut template_text, solidity_pairing_lib) = if is_abiv2 { + fn export_solidity_verifier(&self, vk: Vec, abi_v2: bool) -> String { + let bellman_vk: VerifyingKey = VerifyingKey::read(vk.as_slice()).unwrap(); + let (mut template_text, solidity_pairing_lib) = if abi_v2 { ( String::from(CONTRACT_TEMPLATE_V2), String::from(SOLIDITY_PAIRING_LIB_V2), @@ -81,31 +132,55 @@ impl ProofSystem for G16 { let vk_gamma_abc_repeat_regex = Regex::new(r#"(<%vk_gamma_abc_pts%>)"#).unwrap(); let vk_input_len_regex = Regex::new(r#"(<%vk_input_length%>)"#).unwrap(); - template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.alpha").unwrap().as_str()).into_owned(); - template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.beta").unwrap().as_str()).into_owned(); - template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.gamma").unwrap().as_str()).into_owned(); - template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.delta").unwrap().as_str()).into_owned(); + template_text = vk_regex + .replace( + template_text.as_str(), + parse_g1_hex(&bellman_vk.alpha_g1).as_str(), + ) + .into_owned(); + template_text = vk_regex + .replace( + template_text.as_str(), + parse_g2_hex(&bellman_vk.beta_g2).as_str(), + ) + .into_owned(); + template_text = vk_regex + .replace( + template_text.as_str(), + parse_g2_hex(&bellman_vk.gamma_g2).as_str(), + ) + .into_owned(); + template_text = vk_regex + .replace( + template_text.as_str(), + parse_g2_hex(&bellman_vk.delta_g2).as_str(), + ) + .into_owned(); - let gamma_abc_count_str = vk_map.get("vk.gamma_abc.len()").unwrap(); - let gamma_abc_count: i32 = gamma_abc_count_str.parse::().unwrap(); + let gamma_abc_count: usize = bellman_vk.ic.len(); + template_text = vk_gamma_abc_len_regex + .replace( + template_text.as_str(), + format!("{}", gamma_abc_count).as_str(), + ) + .into_owned(); - template_text = vk_gamma_abc_len_regex.replace( - template_text.as_str(), - format!("{}", gamma_abc_count).as_str() - ).into_owned(); - - template_text = vk_input_len_regex.replace( - template_text.as_str(), - format!("{}", gamma_abc_count - 1).as_str() - ).into_owned(); + template_text = vk_input_len_regex + .replace( + template_text.as_str(), + format!("{}", gamma_abc_count - 1).as_str(), + ) + .into_owned(); let mut gamma_abc_repeat_text = String::new(); for x in 0..gamma_abc_count { gamma_abc_repeat_text.push_str( format!( - "vk.gamma_abc[{}] = Pairing.G1Point({});", x, - vk_map.get(format!("vk.gamma_abc[{}]", x).as_str()).unwrap() - ).as_str() + "vk.gamma_abc[{}] = Pairing.G1Point({});", + x, + parse_g1_hex(bellman_vk.ic.get(x).unwrap()) + ) + .as_str(), ); if x < gamma_abc_count - 1 { gamma_abc_repeat_text.push_str("\n "); @@ -125,61 +200,27 @@ impl ProofSystem for G16 { ) } - fn verify(&self, vk: String, proof: String) -> bool { - unimplemented!() - } -} + fn verify(&self, vk: Vec, proof: String) -> bool { + let vk: VerifyingKey = + VerifyingKey::read(vk.as_slice()).expect("Could not read verifying key"); -mod serialize { - use bellman::groth16::{Proof, VerifyingKey}; - use pairing::bn256::{Bn256, Fr}; + let pvk: PreparedVerifyingKey = prepare_verifying_key(&vk); + let g16_proof: G16Proof = serde_json::from_str(proof.as_str()).unwrap(); - use crate::proof_system::bn128::utils::bellman::{ - parse_fr_json, parse_g1_hex, parse_g1_json, parse_g2_hex, parse_g2_json, - }; + let proof_bytes = base64::decode(g16_proof.raw.as_str()).unwrap(); + let bellman_proof: Proof = Proof::read(proof_bytes.as_slice()).unwrap(); - pub fn serialize_vk(vk: VerifyingKey) -> String { - format!( - "vk.alpha = {} - vk.beta = {} - vk.gamma = {} - vk.delta = {} - vk.gamma_abc.len() = {} - {}", - parse_g1_hex(&vk.alpha_g1), - parse_g2_hex(&vk.beta_g2), - parse_g2_hex(&vk.gamma_g2), - parse_g2_hex(&vk.delta_g2), - vk.ic.len(), - vk.ic - .iter() - .enumerate() - .map(|(i, x)| format!("vk.gamma_abc[{}] = {}", i, parse_g1_hex(x))) - .collect::>() - .join("\n") - ) - } + let public_inputs: Vec = g16_proof + .inputs + .iter() + .map(|s| { + FieldPrime::try_from_hex_str(s.as_str()) + .unwrap() + .into_bellman() + }) + .collect::>(); - pub fn serialize_proof(p: &Proof, inputs: &Vec) -> String { - - format!( - "{{ - \"proof\": {{ - \"a\": {}, - \"b\": {}, - \"c\": {} - }}, - \"inputs\": [{}] - }}", - parse_g1_json(&p.a), - parse_g2_json(&p.b), - parse_g1_json(&p.c), - inputs - .iter() - .map(parse_fr_json) - .collect::>() - .join(", "), - ) + verify_proof(&pvk, &bellman_proof, &public_inputs).unwrap() } } diff --git a/zokrates_core/src/proof_system/bn128/utils/bellman.rs b/zokrates_core/src/proof_system/bn128/utils/bellman.rs index 478e10ff..42ed5400 100644 --- a/zokrates_core/src/proof_system/bn128/utils/bellman.rs +++ b/zokrates_core/src/proof_system/bn128/utils/bellman.rs @@ -224,69 +224,50 @@ mod parse { pub fn parse_g1(e: &::G1Affine) -> (String, String) { let raw_e = e.to_string(); - let captures = G1_REGEX.captures(&raw_e).unwrap(); - ( captures.name(&"x").unwrap().as_str().to_string(), captures.name(&"y").unwrap().as_str().to_string(), ) } - fn parse_g2( + pub fn parse_g2( e: &::G2Affine, - ) -> (String, String, String, String) { + ) -> ((String, String), (String, String)) { let raw_e = e.to_string(); - let captures = G2_REGEX.captures(&raw_e).unwrap(); - ( - captures.name(&"x1").unwrap().as_str().to_string(), - captures.name(&"x0").unwrap().as_str().to_string(), - captures.name(&"y1").unwrap().as_str().to_string(), - captures.name(&"y0").unwrap().as_str().to_string(), + ( + captures.name(&"x1").unwrap().as_str().to_string(), + captures.name(&"x0").unwrap().as_str().to_string(), + ), + ( + captures.name(&"y1").unwrap().as_str().to_string(), + captures.name(&"y0").unwrap().as_str().to_string(), + ), ) } - fn parse_fr(e: &Fr) -> String { + pub fn parse_fr(e: &Fr) -> String { let raw_e = e.to_string(); - let captures = FR_REGEX.captures(&raw_e).unwrap(); - captures.name(&"x").unwrap().as_str().to_string() } - pub fn parse_g1_json(e: &::G1Affine) -> String { - let parsed = parse_g1(e); - - format!("[\"{}\", \"{}\"]", parsed.0, parsed.1) - } - - pub fn parse_g2_json(e: &::G2Affine) -> String { - let parsed = parse_g2(e); - - format!( - "[[\"{}\", \"{}\"], [\"{}\", \"{}\"]]", - parsed.0, parsed.1, parsed.2, parsed.3, - ) - } - - pub fn parse_fr_json(e: &Fr) -> String { - let parsed = parse_fr(e); - - format!("\"{}\"", parsed) - } - pub fn parse_g1_hex(e: &::G1Affine) -> String { let parsed = parse_g1(e); - format!("{}, {}", parsed.0, parsed.1) } pub fn parse_g2_hex(e: &::G2Affine) -> String { let parsed = parse_g2(e); - - format!("[{}, {}], [{}, {}]", parsed.0, parsed.1, parsed.2, parsed.3,) + format!( + "[{}, {}], [{}, {}]", + (parsed.0).0, + (parsed.0).1, + (parsed.1).0, + (parsed.1).1 + ) } } diff --git a/zokrates_core/src/proof_system/bn128/utils/mod.rs b/zokrates_core/src/proof_system/bn128/utils/mod.rs index b137c0c9..1aa80a4e 100644 --- a/zokrates_core/src/proof_system/bn128/utils/mod.rs +++ b/zokrates_core/src/proof_system/bn128/utils/mod.rs @@ -4,4 +4,3 @@ pub mod ffi; #[cfg(feature = "libsnark")] pub mod libsnark; pub mod solidity; -pub mod parser; diff --git a/zokrates_core/src/proof_system/bn128/utils/parser.rs b/zokrates_core/src/proof_system/bn128/utils/parser.rs deleted file mode 100644 index c0e22ec7..00000000 --- a/zokrates_core/src/proof_system/bn128/utils/parser.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::collections::HashMap; - -pub trait KeyValueParser { - fn parse_pairs(&self) -> HashMap; -} - -impl KeyValueParser for String { - - fn parse_pairs(&self) -> HashMap { - let mut map = HashMap::new(); - let mut lines = self.lines(); - - while let Some(current_line) = lines.next() { - let key_value: Vec<&str> = current_line.split("=").collect(); - if key_value.len() < 2 { - continue; - } - map.insert( - String::from(key_value[0].trim()), - String::from(key_value[1].trim_start())); - } - map - } -} - -#[cfg(test)] -mod tests { - use super::KeyValueParser; - - #[test] - fn parse_example() { - let example: &str = r#" - a = 1 - b = 2 - "#; - - let map = String::from(example).parse_pairs(); - - assert_eq!("1", map.get("a").unwrap()); - assert_eq!("2", map.get("b").unwrap()); - } - - #[test] - fn parse_empty() { - let map = String::from("").parse_pairs(); - assert!(map.is_empty()); - } -} \ No newline at end of file diff --git a/zokrates_core/src/proof_system/mod.rs b/zokrates_core/src/proof_system/mod.rs index 6cd255d3..e70caa3d 100644 --- a/zokrates_core/src/proof_system/mod.rs +++ b/zokrates_core/src/proof_system/mod.rs @@ -11,15 +11,15 @@ pub use self::bn128::PGHR13; use crate::ir; // We only need to serialize this struct, there is no need for deserialization as keys are -// used separetely in other use cases +// used separately in other use cases #[derive(Serialize)] pub struct SetupKeypair { - pub vk: String, + pub vk: Vec, pub pk: Vec, } impl SetupKeypair { - pub fn from(vk: String, pk: Vec) -> SetupKeypair { + pub fn from(vk: Vec, pk: Vec) -> SetupKeypair { SetupKeypair { vk, pk } } } @@ -34,7 +34,7 @@ pub trait ProofSystem { proving_key: Vec, ) -> String; - fn export_solidity_verifier(&self, vk: String, is_abiv2: bool) -> String; + fn export_solidity_verifier(&self, vk: Vec, abi_v2: bool) -> String; - fn verify(&self, vk: String, proof: String) -> bool; + fn verify(&self, vk: Vec, proof: String) -> bool; } diff --git a/zokrates_field/src/field.rs b/zokrates_field/src/field.rs index ddcb5244..df8ca054 100644 --- a/zokrates_field/src/field.rs +++ b/zokrates_field/src/field.rs @@ -88,6 +88,7 @@ pub trait Field: fn get_required_bits() -> usize; /// Tries to parse a string into this representation fn try_from_dec_str<'a>(s: &'a str) -> Result; + fn try_from_hex_str<'a>(s: &'a str) -> Result; /// Returns a decimal string representing the member of the equivalence class of this `Field` in Z/pZ /// which lies in [-(p-1)/2, (p-1)/2] fn to_compact_dec_string(&self) -> String; @@ -139,12 +140,19 @@ impl Field for FieldPrime { fn get_required_bits() -> usize { (*P).bits() } - fn try_from_dec_str<'a>(s: &'a str) -> Result { + fn try_from_dec_str(s: &str) -> Result { let x = BigInt::parse_bytes(s.as_bytes(), 10).ok_or(())?; Ok(FieldPrime { value: &x - x.div_floor(&*P) * &*P, }) } + fn try_from_hex_str(s: &str) -> Result { + let x = BigInt::parse_bytes(s.trim_start_matches("0x").as_bytes(), 16) + .ok_or(())?; + Ok(FieldPrime { + value: &x - x.div_floor(&*P) * &*P, + }) + } fn to_compact_dec_string(&self) -> String { // values up to (p-1)/2 included are represented as positive, values between (p+1)/2 and p-1 are represented as negative by subtracting p if self.value <= FieldPrime::max_value().value / 2 {