diff --git a/Cargo.lock b/Cargo.lock index d2281a5d..48654acd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "arbitrary" version = "1.3.0" @@ -97,6 +103,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "blst" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -125,6 +143,48 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chia-bls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f02b3316f347607f79668627cf1f01b5a62257089c08e92c7df3ad93c6c1aa" +dependencies = [ + "anyhow", + "arbitrary", + "blst", + "chia-traits", + "clvm-traits 0.2.14", + "hex", + "hkdf", + "sha2 0.10.8", + "thiserror", + "tiny-bip39", +] + +[[package]] +name = "chia-traits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "636e832ece4dc14e2e866d1737fbfdc32d6dd616aa4fc3869cfb273881def303" +dependencies = [ + "chia_streamable_macro", + "hex", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "chia_streamable_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "342cdb29dd8c1214d60fac24100ab1567756270710e93336db910a531fe8a264" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "ciborium" version = "0.2.1" @@ -189,14 +249,24 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clvm-derive" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9110e638f8a4d34922e0436282c7fe1a8149020aef3aacf699377dcd3f1553da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "clvm-rs-test-tools" version = "0.1.0" dependencies = [ - "bls12_381", + "chia-bls", "clap", - "clvmr", - "group", + "clvmr 0.3.0", "hex", "hex-literal", "linreg", @@ -207,6 +277,18 @@ dependencies = [ "sha1", ] +[[package]] +name = "clvm-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64ffd2241dead56ca31fb0261b4d0f802ba0b98a1c1e6f4514bc06c6095a9a9" +dependencies = [ + "clvm-derive", + "clvmr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint", + "thiserror", +] + [[package]] name = "clvm-traits" version = "0.3.0" @@ -221,7 +303,7 @@ dependencies = [ name = "clvm_rs" version = "0.3.0" dependencies = [ - "clvmr", + "clvmr 0.3.0", "pyo3", ] @@ -229,7 +311,7 @@ dependencies = [ name = "clvm_rs-fuzz" version = "1.0.0" dependencies = [ - "clvmr", + "clvmr 0.3.0", "libfuzzer-sys", ] @@ -237,7 +319,7 @@ dependencies = [ name = "clvm_wasm" version = "0.3.0" dependencies = [ - "clvmr", + "clvmr 0.3.0", "js-sys", "wasm-bindgen", "wasm-bindgen-test", @@ -247,11 +329,9 @@ dependencies = [ name = "clvmr" version = "0.3.0" dependencies = [ - "bls12_381", - "clvm-traits", + "chia-bls", + "clvm-traits 0.3.0", "criterion", - "getrandom", - "group", "hex", "k256", "lazy_static", @@ -261,6 +341,25 @@ dependencies = [ "openssl", "p256", "rstest", + "sha2 0.10.8", +] + +[[package]] +name = "clvmr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd344b6dc76235f446025fe9ebe54aa6131e2e59acb49e16be48a3bb3492491" +dependencies = [ + "bls12_381", + "getrandom", + "group", + "hex", + "k256", + "lazy_static", + "num-bigint", + "num-integer", + "num-traits", + "p256", "sha2 0.9.9", ] @@ -473,6 +572,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.2" @@ -645,6 +750,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "group" version = "0.13.0" @@ -662,6 +773,12 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.1" @@ -686,6 +803,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -695,6 +821,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "indoc" version = "1.0.9" @@ -755,7 +891,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.7", + "sha2 0.10.8", "signature", ] @@ -959,7 +1095,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -994,6 +1130,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1074,6 +1219,16 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1308,6 +1463,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1434,9 +1595,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -1553,6 +1714,34 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2", + "rand", + "rustc-hash", + "sha2 0.10.8", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -1563,6 +1752,38 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" version = "1.16.0" @@ -1575,6 +1796,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unindent" version = "0.1.11" @@ -1806,6 +2036,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "winnow" +version = "0.5.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e87b8dfbe3baffbe687eef2e164e32286eff31a5ee16463ce03d991643ec94" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.5.1" @@ -1820,3 +2059,17 @@ name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] diff --git a/Cargo.toml b/Cargo.toml index 8e1d7957..72a21f6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,26 +29,22 @@ pre-eval = [] lto = true [dependencies] -hex = "=0.4.3" -lazy_static = "=1.4.0" -num-bigint = "=0.4.3" -num-traits = "=0.2.15" -num-integer = "=0.1.45" +hex = "0.4.3" +lazy_static = "1.4.0" +num-bigint = "0.4.3" +num-traits = "0.2.15" +num-integer = "0.1.45" clvm-traits = "0.3.0" -# the experimental feature enables hashing to curves -bls12_381 = { version = "=0.8.0", features = ["experimental"] } -# the newer sha2 crate doesn't implement the digest traits required by HKDF -group = "=0.13.0" -sha2 = "=0.9.9" +chia-bls = "0.3.0" +sha2 = "0.10.8" openssl = { version = "=0.10.55", features = ["vendored"], optional = true } -getrandom = { version = "=0.2.9", features = ["js" ]} # for secp sigs k256 = { version = "0.13.1", features = ["ecdsa"] } p256 = { version = "0.13.2", features = ["ecdsa"] } [dev-dependencies] -rstest = "=0.17.0" -criterion = "=0.5.1" +rstest = "0.17.0" +criterion = "0.5.1" [[bench]] name = "run-program" diff --git a/src/allocator.rs b/src/allocator.rs index c2df666b..1bb63420 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -1,7 +1,7 @@ use crate::err_utils::err; use crate::number::{node_from_number, number_from_u8, Number}; use crate::reduction::EvalErr; -use bls12_381::{G1Affine, G1Projective, G2Affine, G2Projective}; +use chia_bls::{G1Element, G2Element}; use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvmError, ToClvmError}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -149,14 +149,12 @@ impl Allocator { node_from_number(self, &v) } - pub fn new_g1(&mut self, g1: G1Projective) -> Result { - let g1: G1Affine = g1.into(); - self.new_atom(&g1.to_compressed()) + pub fn new_g1(&mut self, g1: G1Element) -> Result { + self.new_atom(&g1.to_bytes()) } - pub fn new_g2(&mut self, g2: G2Projective) -> Result { - let g2: G2Affine = g2.into(); - self.new_atom(&g2.to_compressed()) + pub fn new_g2(&mut self, g2: G2Element) -> Result { + self.new_atom(&g2.to_bytes()) } pub fn new_pair(&mut self, first: NodePtr, rest: NodePtr) -> Result { @@ -252,42 +250,32 @@ impl Allocator { number_from_u8(self.atom(node)) } - pub fn g1(&self, node: NodePtr) -> Result { + pub fn g1(&self, node: NodePtr) -> Result { let blob = match self.sexp(node) { SExp::Atom => self.atom(node), _ => { return err(node, "pair found, expected G1 point"); } }; - if blob.len() != 48 { - return err(node, "atom is not G1 size, 48 bytes"); - } - - let affine: Option = - G1Affine::from_compressed(blob.try_into().expect("G1 slice is not 48 bytes")).into(); - match affine { - Some(point) => Ok(G1Projective::from(point)), - None => err(node, "atom is not a G1 point"), - } + let array: [u8; 48] = blob + .try_into() + .map_err(|_| EvalErr(node, "atom is not G1 size, 48 bytes".to_string()))?; + G1Element::from_bytes(&array) + .map_err(|_| EvalErr(node, "atom is not a G1 point".to_string())) } - pub fn g2(&self, node: NodePtr) -> Result { + pub fn g2(&self, node: NodePtr) -> Result { let blob = match self.sexp(node) { SExp::Atom => self.atom(node), _ => { return err(node, "pair found, expected G2 point"); } }; - if blob.len() != 96 { - return err(node, "atom is not G2 size, 96 bytes"); - } - - let affine: Option = - G2Affine::from_compressed(blob.try_into().expect("G2 slice is not 96 bytes")).into(); - match affine { - Some(point) => Ok(G2Projective::from(point)), - None => err(node, "atom is not a G2 point"), - } + let array = blob + .try_into() + .map_err(|_| EvalErr(node, "atom is not G2 size, 96 bytes".to_string()))?; + G2Element::from_bytes(&array) + .map_err(|_| EvalErr(node, "atom is not a G2 point".to_string())) } pub fn sexp(&self, node: NodePtr) -> SExp { @@ -756,7 +744,7 @@ fn test_g1_roundtrip(#[case] atom: &str) { let mut a = Allocator::new(); let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap(); let g1 = a.g1(n).unwrap(); - assert_eq!(hex::encode(G1Affine::from(g1).to_compressed()), atom); + assert_eq!(hex::encode(g1.to_bytes()), atom); let g1_copy = a.new_g1(g1).unwrap(); let g1_atom = a.atom(g1_copy); @@ -801,7 +789,7 @@ fn test_g2_roundtrip(#[case] atom: &str) { let mut a = Allocator::new(); let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap(); let g2 = a.g2(n).unwrap(); - assert_eq!(hex::encode(G2Affine::from(g2).to_compressed()), atom); + assert_eq!(hex::encode(g2.to_bytes()), atom); let g2_copy = a.new_g2(g2).unwrap(); let g2_atom = a.atom(g2_copy); @@ -841,31 +829,25 @@ fn make_number(a: &mut Allocator, bytes: &[u8]) -> NodePtr { #[cfg(test)] fn make_g1(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - let v: G1Projective = G1Affine::from_compressed(bytes.try_into().unwrap()) - .unwrap() - .into(); + let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap(); a.new_g1(v).unwrap() } #[cfg(test)] fn make_g2(a: &mut Allocator, bytes: &[u8]) -> NodePtr { - let v: G2Projective = G2Affine::from_compressed(bytes.try_into().unwrap()) - .unwrap() - .into(); + let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap(); a.new_g2(v).unwrap() } #[cfg(test)] fn make_g1_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr { assert!(<[u8; 48]>::try_from(bytes).is_err()); - //assert!(G1Affine::from_compressed(bytes.try_into().unwrap()).is_none().unwrap_u8() != 0); a.new_atom(bytes).unwrap() } #[cfg(test)] fn make_g2_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr { assert!(<[u8; 96]>::try_from(bytes).is_err()); - //assert!(G2Affine::from_compressed(bytes.try_into().unwrap()).is_none().unwrap_u8() != 0); a.new_atom(bytes).unwrap() } @@ -888,32 +870,26 @@ fn check_number(a: &Allocator, n: NodePtr, bytes: &[u8]) { #[cfg(test)] fn check_g1(a: &Allocator, n: NodePtr, bytes: &[u8]) { let num = a.g1(n).unwrap(); - let v: G1Projective = G1Affine::from_compressed(bytes.try_into().unwrap()) - .unwrap() - .into(); + let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap(); assert_eq!(num, v); } #[cfg(test)] fn check_g2(a: &Allocator, n: NodePtr, bytes: &[u8]) { let num = a.g2(n).unwrap(); - let v: G2Projective = G2Affine::from_compressed(bytes.try_into().unwrap()) - .unwrap() - .into(); + let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap(); assert_eq!(num, v); } #[cfg(test)] fn check_g1_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) { assert_eq!(a.g1(n).unwrap_err().0, n); - //assert!(G1Affine::from_compressed(bytes.try_into().unwrap()).is_none().unwrap_u8() != 0); assert!(<[u8; 48]>::try_from(bytes).is_err()); } #[cfg(test)] fn check_g2_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) { assert_eq!(a.g2(n).unwrap_err().0, n); - //assert!(G2Affine::from_compressed(bytes.try_into().unwrap()).is_none().unwrap_u8() != 0); assert!(<[u8; 96]>::try_from(bytes).is_err()); } @@ -1079,8 +1055,7 @@ e28f75bb8f1c7c42c39a8c5529bf0f4e", fn test_atom_len_g1(#[case] buffer_hex: &str, #[case] expected: usize) { let mut a = Allocator::new(); let buffer = &hex::decode(buffer_hex).unwrap(); - let g1 = - G1Projective::from(G1Affine::from_compressed(&buffer[..].try_into().unwrap()).unwrap()); + let g1 = G1Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G1 point"); let atom = a.new_g1(g1).unwrap(); assert_eq!(a.atom_len(atom), expected); } @@ -1111,8 +1086,7 @@ fn test_atom_len_g2(#[case] buffer_hex: &str, #[case] expected: usize) { let mut a = Allocator::new(); let buffer = &hex::decode(buffer_hex).unwrap(); - let g2 = - G2Projective::from(G2Affine::from_compressed(&buffer[..].try_into().unwrap()).unwrap()); + let g2 = G2Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G2 point"); let atom = a.new_g2(g2).unwrap(); assert_eq!(a.atom_len(atom), expected); } diff --git a/src/bls_ops.rs b/src/bls_ops.rs index 0271441b..2142377e 100644 --- a/src/bls_ops.rs +++ b/src/bls_ops.rs @@ -2,14 +2,14 @@ use crate::allocator::{Allocator, NodePtr}; use crate::cost::{check_cost, Cost}; use crate::err_utils::err; use crate::op_utils::{ - atom, first, get_args, get_varargs, int_atom, mod_group_order, new_atom_and_cost, nullp, - number_to_scalar, rest, MALLOC_COST_PER_BYTE, + atom, first, get_args, get_varargs, int_atom, mod_group_order, new_atom_and_cost, nullp, rest, + MALLOC_COST_PER_BYTE, +}; +use crate::reduction::{EvalErr, Reduction, Response}; +use chia_bls::{ + aggregate_pairing, aggregate_verify, hash_to_g1_with_dst, hash_to_g2_with_dst, G1Element, + G2Element, PublicKey, }; -use crate::reduction::{Reduction, Response}; -use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve}; -use bls12_381::{multi_miller_loop, G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective}; -use group::Group; -use std::ops::Neg; // the same cost as point_add (aka g1_add) const BLS_G1_SUBTRACT_BASE_COST: Cost = 101094; @@ -51,7 +51,7 @@ const DST_G2: &[u8; 43] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"; pub fn op_bls_g1_subtract(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> Response { let mut cost = BLS_G1_SUBTRACT_BASE_COST; check_cost(a, cost, max_cost)?; - let mut total: G1Projective = G1Projective::identity(); + let mut total = G1Element::default(); let mut is_first = true; while let Some((arg, rest)) = a.next(input) { input = rest; @@ -61,7 +61,7 @@ pub fn op_bls_g1_subtract(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) if is_first { total = point; } else { - total -= point; + total -= &point; }; is_first = false; } @@ -82,7 +82,8 @@ pub fn op_bls_g1_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> cost += scalar_len as Cost * BLS_G1_MULTIPLY_COST_PER_BYTE; check_cost(a, cost, max_cost)?; - total *= number_to_scalar(mod_group_order(scalar)); + let scalar = mod_group_order(scalar); + total.scalar_multiply(scalar.to_bytes_be().1.as_slice()); Ok(Reduction( cost + 48 * MALLOC_COST_PER_BYTE, @@ -93,18 +94,14 @@ pub fn op_bls_g1_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> pub fn op_bls_g1_negate(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response { let [point] = get_args::<1>(a, input, "g1_negate")?; - // we don't validate the point. We may want to soft fork-in validating the - // point once the allocator preserves native representation of points let blob = atom(a, point, "G1 atom")?; - if blob.len() != 48 { - return err(point, "atom is not G1 size, 48 bytes"); - } - if G1Affine::from_compressed(blob.try_into().expect("G1 slice is not 48 bytes")) - .is_none() - .into() - { - return err(point, "atom is not a valid G1 point"); - } + // this is here to validate the point + let _g1 = G1Element::from_bytes( + blob.try_into() + .map_err(|_| EvalErr(point, "atom is not G1 size, 48 bytes".to_string()))?, + ) + .map_err(|_| EvalErr(point, "atom is not a valid G1 point".to_string()))?; + if (blob[0] & 0xe0) == 0xc0 { // This is compressed infinity. negating it is a no-op // we can just pass through the same atom as we received. We'll charge @@ -123,7 +120,7 @@ pub fn op_bls_g1_negate(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> R pub fn op_bls_g2_add(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> Response { let mut cost = BLS_G2_ADD_BASE_COST; check_cost(a, cost, max_cost)?; - let mut total: G2Projective = G2Projective::identity(); + let mut total = G2Element::default(); while let Some((arg, rest)) = a.next(input) { input = rest; let point = a.g2(arg)?; @@ -140,7 +137,7 @@ pub fn op_bls_g2_add(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> R pub fn op_bls_g2_subtract(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> Response { let mut cost = BLS_G2_SUBTRACT_BASE_COST; check_cost(a, cost, max_cost)?; - let mut total: G2Projective = G2Projective::identity(); + let mut total = G2Element::default(); let mut is_first = true; while let Some((arg, rest)) = a.next(input) { input = rest; @@ -150,7 +147,7 @@ pub fn op_bls_g2_subtract(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) if is_first { total = point; } else { - total -= point; + total -= &point; }; is_first = false; } @@ -171,7 +168,8 @@ pub fn op_bls_g2_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> cost += scalar_len as Cost * BLS_G2_MULTIPLY_COST_PER_BYTE; check_cost(a, cost, max_cost)?; - total *= number_to_scalar(mod_group_order(scalar)); + let scalar = mod_group_order(scalar); + total.scalar_multiply(scalar.to_bytes_be().1.as_slice()); Ok(Reduction( cost + 96 * MALLOC_COST_PER_BYTE, @@ -185,16 +183,14 @@ pub fn op_bls_g2_negate(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> R // we don't validate the point. We may want to soft fork-in validating the // point once the allocator preserves native representation of points let blob = atom(a, point, "G2 atom")?; - if blob.len() != 96 { - return err(point, "atom is not G2 size, 96 bytes"); - } - if G2Affine::from_compressed(blob.try_into().expect("G2 slice is not 96 bytes")) - .is_none() - .into() - { - return err(point, "atom is not a valid G2 point"); - } + // this is here to validate the point + let _g2 = G2Element::from_bytes( + blob.try_into() + .map_err(|_| EvalErr(point, "atom is not G2 size, 96 bytes".to_string()))?, + ) + .map_err(|_| EvalErr(point, "atom is not a valid G2 point".to_string()))?; + if (blob[0] & 0xe0) == 0xc0 { // This is compressed infinity. negating it is a no-op // we can just pass through the same atom as we received. We'll charge @@ -231,7 +227,7 @@ pub fn op_bls_map_to_g1(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Re cost += dst.len() as Cost * BLS_MAP_TO_G1_COST_PER_DST_BYTE; check_cost(a, cost, max_cost)?; - let point = >>::hash_to_curve(msg, dst); + let point = hash_to_g1_with_dst(msg, dst); Ok(Reduction( cost + 48 * MALLOC_COST_PER_BYTE, a.new_g1(point)?, @@ -258,7 +254,7 @@ pub fn op_bls_map_to_g2(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Re cost += dst.len() as Cost * BLS_MAP_TO_G2_COST_PER_DST_BYTE; check_cost(a, cost, max_cost)?; - let point = >>::hash_to_curve(msg, dst); + let point = hash_to_g2_with_dst(msg, dst); Ok(Reduction( cost + 96 * MALLOC_COST_PER_BYTE, a.new_g2(point)?, @@ -268,13 +264,12 @@ pub fn op_bls_map_to_g2(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Re // This operator takes a variable number of G1 and G2 points. The points must // come in pairs (as a "flat" argument list). // It performs a low-level pairing operation of the (G1, G2)-pairs -// and returns a boolean indicating whether the resulting Gt point is the -// identity or not. True means identity False otherwise. This is a building -// block for signature verification. +// and returns if the resulting Gt point is the +// identity, otherwise terminates the program with a validation error. pub fn op_bls_pairing_identity(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { let mut cost = BLS_PAIRING_BASE_COST; check_cost(a, cost, max_cost)?; - let mut items = Vec::<(G1Affine, G2Prepared)>::new(); + let mut items = Vec::<(G1Element, G2Element)>::new(); let mut args = input; while !nullp(a, args) { @@ -284,18 +279,10 @@ pub fn op_bls_pairing_identity(a: &mut Allocator, input: NodePtr, max_cost: Cost args = rest(a, args)?; let g2 = a.g2(first(a, args)?)?; args = rest(a, args)?; - items.push((g1.into(), G2Prepared::from(G2Affine::from(g2)))); + items.push((g1, g2)); } - let mut item_refs = Vec::<(&G1Affine, &G2Prepared)>::new(); - for (p, q) in &items { - item_refs.push((p, q)); - } - let identity: bool = multi_miller_loop(&item_refs) - .final_exponentiation() - .is_identity() - .into(); - if !identity { + if !aggregate_pairing(items) { err(input, "bls_pairing_identity failed") } else { Ok(Reduction(cost, a.null())) @@ -318,7 +305,7 @@ pub fn op_bls_verify(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Respo // followed by a variable number of (G1, msg)-pairs (as a flat list) args = rest(a, args)?; - let mut items = Vec::<(G1Affine, G2Prepared)>::new(); + let mut items = Vec::<(PublicKey, &[u8])>::new(); while !nullp(a, args) { let pk = a.g1(first(a, args)?)?; args = rest(a, args)?; @@ -330,32 +317,10 @@ pub fn op_bls_verify(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Respo cost += DST_G2.len() as Cost * BLS_MAP_TO_G2_COST_PER_DST_BYTE; check_cost(a, cost, max_cost)?; - // The AUG scheme requires prepending the public key to the signed - // message - let mut prepended_msg = G1Affine::from(pk).to_compressed().to_vec(); - prepended_msg.extend_from_slice(msg); - - let point = >>::hash_to_curve( - prepended_msg, - DST_G2, - ); - items.push((pk.into(), G2Prepared::from(G2Affine::from(point)))); + items.push((pk, msg)); } - items.push(( - G1Affine::generator().neg(), - G2Prepared::from(G2Affine::from(signature)), - )); - - let mut item_refs = Vec::<(&G1Affine, &G2Prepared)>::new(); - for (p, q) in &items { - item_refs.push((p, q)); - } - let identity: bool = multi_miller_loop(&item_refs) - .final_exponentiation() - .is_identity() - .into(); - if !identity { + if !aggregate_verify(&signature, items) { err(input, "bls_verify failed") } else { Ok(Reduction(cost, a.null())) diff --git a/src/more_ops.rs b/src/more_ops.rs index ffea2185..b09d75ca 100644 --- a/src/more_ops.rs +++ b/src/more_ops.rs @@ -1,4 +1,3 @@ -use bls12_381::{G1Affine, G1Projective, Scalar}; use num_bigint::{BigUint, Sign}; use num_integer::Integer; use std::ops::BitAndAssign; @@ -11,10 +10,11 @@ use crate::err_utils::err; use crate::number::Number; use crate::op_utils::{ atom, atom_len, get_args, get_varargs, i32_atom, int_atom, mod_group_order, new_atom_and_cost, - nullp, number_to_scalar, u32_from_u8, MALLOC_COST_PER_BYTE, + nullp, u32_from_u8, MALLOC_COST_PER_BYTE, }; use crate::reduction::{Reduction, Response}; use crate::sha2::{Digest, Sha256}; +use chia_bls::G1Element; const ARITH_BASE_COST: Cost = 99; const ARITH_COST_PER_ARG: Cost = 320; @@ -790,10 +790,11 @@ pub fn op_all(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> Response pub fn op_pubkey_for_exp(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response { let [n] = get_args::<1>(a, input, "pubkey_for_exp")?; let (v0, v0_len) = int_atom(a, n, "pubkey_for_exp")?; - let exp: Number = mod_group_order(v0); + let bytes = mod_group_order(v0).to_bytes_be().1; + + let point = G1Element::from_integer(&bytes); + let cost = PUBKEY_BASE_COST + (v0_len as Cost) * PUBKEY_COST_PER_BYTE; - let exp: Scalar = number_to_scalar(exp); - let point: G1Projective = G1Affine::generator() * exp; Ok(Reduction( cost + 48 * MALLOC_COST_PER_BYTE, a.new_g1(point)?, @@ -802,7 +803,7 @@ pub fn op_pubkey_for_exp(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> pub fn op_point_add(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> Response { let mut cost = POINT_ADD_BASE_COST; - let mut total: G1Projective = G1Projective::identity(); + let mut total = G1Element::default(); while let Some((arg, rest)) = a.next(input) { input = rest; let point = a.g1(arg)?; diff --git a/src/op_utils.rs b/src/op_utils.rs index c057685c..d3f2cda7 100644 --- a/src/op_utils.rs +++ b/src/op_utils.rs @@ -4,7 +4,6 @@ use crate::err_utils::err; use crate::number::Number; use crate::reduction::EvalErr; use crate::reduction::{Reduction, Response}; -use bls12_381::Scalar; use lazy_static::lazy_static; use num_bigint::{BigUint, Sign}; use num_integer::Integer; @@ -575,18 +574,6 @@ fn test_i32_atom() { assert_eq!(r.1, "test requires int32 args (with no leading zeros)"); } -pub fn number_to_scalar(n: Number) -> Scalar { - let (sign, as_u8): (Sign, Vec) = n.to_bytes_le(); - let mut scalar_array: [u8; 32] = [0; 32]; - scalar_array[..as_u8.len()].clone_from_slice(&as_u8[..]); - let exp: Scalar = Scalar::from_bytes(&scalar_array).unwrap(); - if sign == Sign::Minus { - exp.neg() - } else { - exp - } -} - pub fn new_atom_and_cost(a: &mut Allocator, cost: Cost, buf: &[u8]) -> Response { let c = buf.len() as Cost * MALLOC_COST_PER_BYTE; Ok(Reduction(cost + c, a.new_atom(buf)?)) diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 7c242bb4..21f783d2 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -19,11 +19,10 @@ rand = "0.8.4" sha1 = "=0.10.5" linreg = "=0.2.0" clvmr = { path = ".." } -bls12_381 = { version = "=0.8.0", features = ["experimental"] } +chia-bls = ">=0.2.13" num-bigint = "0.4.3" serde = { version ="1.0.163", features = ["derive"] } serde_json = "1.0.96" -group = "=0.13.0" clap = { version = "=4.0.29", features = ["derive"] } [[bin]] diff --git a/tools/src/bin/verify-zksnark.rs b/tools/src/bin/verify-zksnark.rs index 3d4d11f8..63330f55 100644 --- a/tools/src/bin/verify-zksnark.rs +++ b/tools/src/bin/verify-zksnark.rs @@ -1,6 +1,4 @@ -use bls12_381::{multi_miller_loop, G1Affine, G1Projective, G2Affine, G2Prepared, Scalar}; -use group::Group; - +use chia_bls::{aggregate_pairing, G1Element, G2Element}; use num_bigint::BigInt; use serde::Deserialize; @@ -52,18 +50,23 @@ fn vec_pair(arr: &[String]) -> ([u8; 48], [u8; 48]) { ) } -fn vec_pair_g1(arr: &[String]) -> G1Affine { +fn vec_pair_g1(arr: &[String]) -> G1Element { let (fp_1, fp_2) = vec_pair(arr); let data: [u8; 96] = [fp_1, fp_2].concat().try_into().unwrap(); - G1Affine::from_uncompressed(&data).unwrap() + println!("G1 uncompressed: {}", hex::encode(data)); + let ret = G1Element::from_uncompressed(&data).unwrap(); + println!("G1 compressed: {}", hex::encode(ret.to_bytes())); + ret } -fn vec_pair_g2(arr: &[Vec]) -> G2Affine { +fn vec_pair_g2(arr: &[Vec]) -> G2Element { let (fp_1, fp_2) = vec_pair(&arr[0]); let (fp_3, fp_4) = vec_pair(&arr[1]); let data: [u8; 192] = [fp_2, fp_1, fp_4, fp_3].concat().try_into().unwrap(); - let p = G2Affine::from_uncompressed(&data); - p.unwrap() + println!("G2 uncompressed: {}", hex::encode(data)); + let ret = G2Element::from_uncompressed(&data).unwrap(); + println!("G2 compressed: {}", hex::encode(ret.to_bytes())); + ret } pub fn main() { @@ -91,22 +94,17 @@ pub fn main() { let ic0 = vec_pair_g1(&verification_key.ic[0]); - let mut cpub = G1Affine::identity(); + let mut cpub = G1Element::default(); for (i, public_i) in public.iter().enumerate() { - let ic = vec_pair_g1(&verification_key.ic[i + 1]); - let scalar: [u8; 32] = public_i - .parse::() - .unwrap() - .to_bytes_le() - .1 - .try_into() - .unwrap(); - let scalar = Scalar::from_bytes(&scalar).unwrap(); - cpub = (cpub + ic * scalar).into(); + let mut ic = vec_pair_g1(&verification_key.ic[i + 1]); + let scalar = public_i.parse::().unwrap().to_bytes_be().1; + ic.scalar_multiply(&scalar); + cpub += ⁣ } - cpub = (cpub + G1Projective::from(ic0)).into(); + cpub += &ic0; - let pi_a = vec_pair_g1(&proof.pi_a); + let mut pi_a = vec_pair_g1(&proof.pi_a); + pi_a.negate(); let pi_b = vec_pair_g2(&proof.pi_b); let pi_c = vec_pair_g1(&proof.pi_c); @@ -118,30 +116,23 @@ pub fn main() { // output the compressed values println!( "bls_pairing_identity 0x{} 0x{} 0x{} 0x{} 0x{} 0x{} 0x{} 0x{} => 0 | 7800000", - hex::encode((-pi_a).to_compressed()), - hex::encode(pi_b.to_compressed()), - hex::encode(cpub.to_compressed()), - hex::encode(vk_gamma_2.to_compressed()), - hex::encode(pi_c.to_compressed()), - hex::encode(vk_delta_2.to_compressed()), - hex::encode(vk_alpha_1.to_compressed()), - hex::encode(vk_beta_2.to_compressed()) + hex::encode(pi_a.to_bytes()), + hex::encode(pi_b.to_bytes()), + hex::encode(cpub.to_bytes()), + hex::encode(vk_gamma_2.to_bytes()), + hex::encode(pi_c.to_bytes()), + hex::encode(vk_delta_2.to_bytes()), + hex::encode(vk_alpha_1.to_bytes()), + hex::encode(vk_beta_2.to_bytes()) ); // run the miller loop - let mut item_refs = Vec::<(&G1Affine, &G2Prepared)>::new(); - let pi_a = -pi_a; - let pi_b = G2Prepared::from(pi_b); - let vk_gamma_2 = G2Prepared::from(vk_gamma_2); - let vk_delta_2 = G2Prepared::from(vk_delta_2); - let vk_beta_2 = G2Prepared::from(vk_beta_2); - item_refs.push((&pi_a, &pi_b)); - item_refs.push((&cpub, &vk_gamma_2)); - item_refs.push((&pi_c, &vk_delta_2)); - item_refs.push((&vk_alpha_1, &vk_beta_2)); - let identity: bool = multi_miller_loop(&item_refs) - .final_exponentiation() - .is_identity() - .into(); + let item_refs: Vec<(&G1Element, &G2Element)> = vec![ + (&pi_a, &pi_b), + (&cpub, &vk_gamma_2), + (&pi_c, &vk_delta_2), + (&vk_alpha_1, &vk_beta_2), + ]; + let identity: bool = aggregate_pairing(item_refs); assert!(identity); }