From bafe47e6a7a17df293c37a5fdb8ed8311f57b4e8 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 7 Dec 2023 19:12:55 +0300 Subject: [PATCH 1/3] MIR: Add bls12_381_* types --- contrib/mir/src/ast.rs | 9 ++++++++- contrib/mir/src/typechecker.rs | 9 +++++++++ contrib/mir/src/typechecker/type_props.rs | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/contrib/mir/src/ast.rs b/contrib/mir/src/ast.rs index 7ddb6cc5e3a3..7fee222ba6e9 100644 --- a/contrib/mir/src/ast.rs +++ b/contrib/mir/src/ast.rs @@ -94,6 +94,9 @@ pub enum Type { Lambda(Rc<(Type, Type)>), Ticket(Rc), Timestamp, + Bls12381Fr, + Bls12381G1, + Bls12381G2, } impl Type { @@ -103,7 +106,8 @@ impl Type { use Type::*; match self { Nat | Int | Bool | Mutez | String | Unit | Never | Operation | Address | ChainId - | Bytes | Key | Signature | KeyHash | Timestamp => 1, + | Bytes | Key | Signature | KeyHash | Timestamp | Bls12381Fr | Bls12381G1 + | Bls12381G2 => 1, Pair(p) | Or(p) | Map(p) | Lambda(p) => 1 + p.0.size_for_gas() + p.1.size_for_gas(), Option(x) | List(x) | Set(x) | Contract(x) | Ticket(x) => 1 + x.size_for_gas(), } @@ -184,6 +188,9 @@ impl<'a> IntoMicheline<'a> for &'_ Type { Timestamp => Micheline::prim0(Prim::timestamp), KeyHash => Micheline::prim0(Prim::key_hash), Never => Micheline::prim0(Prim::never), + Bls12381Fr => Micheline::prim0(Prim::bls12_381_fr), + Bls12381G1 => Micheline::prim0(Prim::bls12_381_g1), + Bls12381G2 => Micheline::prim0(Prim::bls12_381_g2), Option(x) => Micheline::prim1( arena, diff --git a/contrib/mir/src/typechecker.rs b/contrib/mir/src/typechecker.rs index ab5241157294..a2e0485cb937 100644 --- a/contrib/mir/src/typechecker.rs +++ b/contrib/mir/src/typechecker.rs @@ -394,6 +394,15 @@ fn parse_ty_with_entrypoints( App(signature, [], _) => Type::Signature, App(signature, ..) => unexpected()?, + App(bls12_381_fr, [], _) => Type::Bls12381Fr, + App(bls12_381_fr, ..) => unexpected()?, + + App(bls12_381_g1, [], _) => Type::Bls12381G1, + App(bls12_381_g1, ..) => unexpected()?, + + App(bls12_381_g2, [], _) => Type::Bls12381G2, + App(bls12_381_g2, ..) => unexpected()?, + Seq(..) | micheline_fields!() | micheline_instructions!() diff --git a/contrib/mir/src/typechecker/type_props.rs b/contrib/mir/src/typechecker/type_props.rs index 70a6efd3fb05..5997b2416b43 100644 --- a/contrib/mir/src/typechecker/type_props.rs +++ b/contrib/mir/src/typechecker/type_props.rs @@ -50,6 +50,15 @@ impl Type { | TypeProperty::Packable => return invalid_type_prop(), TypeProperty::Passable | TypeProperty::Storable | TypeProperty::BigMapValue => (), }, + Bls12381Fr | Bls12381G1 | Bls12381G2 => match prop { + TypeProperty::Comparable => return invalid_type_prop(), + TypeProperty::Passable + | TypeProperty::Storable + | TypeProperty::Pushable + | TypeProperty::Packable + | TypeProperty::BigMapValue + | TypeProperty::Duplicable => (), + }, Operation => match prop { TypeProperty::Comparable | TypeProperty::Passable -- GitLab From 2ff5de015abd69c2b4a519226e3f161d6122865e Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 7 Dec 2023 19:13:15 +0300 Subject: [PATCH 2/3] MIR: utility types for bls12_381 arithmetic --- contrib/mir/Cargo.lock | 1 + contrib/mir/Cargo.toml | 1 + contrib/mir/src/bls.rs | 13 ++ contrib/mir/src/bls/fr.rs | 304 +++++++++++++++++++++++++++++++ contrib/mir/src/bls/g1.rs | 219 ++++++++++++++++++++++ contrib/mir/src/bls/g2.rs | 222 ++++++++++++++++++++++ contrib/mir/src/bls/instances.rs | 36 ++++ contrib/mir/src/lib.rs | 1 + 8 files changed, 797 insertions(+) create mode 100644 contrib/mir/src/bls.rs create mode 100644 contrib/mir/src/bls/fr.rs create mode 100644 contrib/mir/src/bls/g1.rs create mode 100644 contrib/mir/src/bls/g2.rs create mode 100644 contrib/mir/src/bls/instances.rs diff --git a/contrib/mir/Cargo.lock b/contrib/mir/Cargo.lock index a7e2705ab0b7..fd0763c366ab 100644 --- a/contrib/mir/Cargo.lock +++ b/contrib/mir/Cargo.lock @@ -797,6 +797,7 @@ name = "mir" version = "0.1.0" dependencies = [ "base58 0.2.0", + "blst", "checked", "chrono", "cryptoxide", diff --git a/contrib/mir/Cargo.toml b/contrib/mir/Cargo.toml index 921a60361d0b..16044ffcdd48 100644 --- a/contrib/mir/Cargo.toml +++ b/contrib/mir/Cargo.toml @@ -21,6 +21,7 @@ num-bigint = "0.3" num-traits = "0.2" chrono = "0.4" integer-sqrt = "0.1" +blst = "0.3" [[bin]] name = "tzt_runner" diff --git a/contrib/mir/src/bls.rs b/contrib/mir/src/bls.rs new file mode 100644 index 000000000000..6790b92acc4d --- /dev/null +++ b/contrib/mir/src/bls.rs @@ -0,0 +1,13 @@ +/******************************************************************************/ +/* */ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) [2023] Serokell */ +/* */ +/******************************************************************************/ + +pub mod fr; +pub mod g1; +pub mod g2; +mod instances; + +pub use self::{fr::Fr, g1::G1, g2::G2}; diff --git a/contrib/mir/src/bls/fr.rs b/contrib/mir/src/bls/fr.rs new file mode 100644 index 000000000000..cb60674aefd7 --- /dev/null +++ b/contrib/mir/src/bls/fr.rs @@ -0,0 +1,304 @@ +/******************************************************************************/ +/* */ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) [2023] Serokell */ +/* */ +/******************************************************************************/ + +use std::mem::MaybeUninit; + +use blst::*; +use num_bigint::{BigInt, Sign}; +use num_traits::One; +use std::ops::{Add, Mul, Neg}; + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Fr(blst_fr); + +impl Fr { + pub const BYTE_SIZE: usize = 32; + + pub fn from_bytes(bs: &[u8]) -> Option { + if bs.len() > Self::BYTE_SIZE { + return None; + } + let mut buf: [u8; Self::BYTE_SIZE] = [0; Self::BYTE_SIZE]; + buf[0..bs.len()].copy_from_slice(bs); + let mbfr = unsafe { + let mut scalar = MaybeUninit::uninit(); + blst_scalar_from_lendian(scalar.as_mut_ptr(), buf.as_ptr()); + let scalar = scalar.assume_init(); + if blst_scalar_fr_check(&scalar) { + let mut fr = MaybeUninit::uninit(); + blst_fr_from_scalar(fr.as_mut_ptr(), &scalar); + Some(fr.assume_init()) + } else { + None + } + }; + mbfr.map(Self) + } + + pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] { + let mut out = [0; Self::BYTE_SIZE]; + let mut scalar = MaybeUninit::uninit(); + unsafe { + blst_scalar_from_fr(scalar.as_mut_ptr(), &self.0); + blst_lendian_from_scalar(out.as_mut_ptr(), &scalar.assume_init()); + } + out + } + + fn order() -> &'static BigInt { + // 52435875175126190479447740508185965837690552500527637822603658699938581184513, + // the group order + static MEM: std::sync::OnceLock = std::sync::OnceLock::new(); + MEM.get_or_init(|| { + BigInt::from_slice( + Sign::Plus, + &[ + 1, 4294967295, 4294859774, 1404937218, 161601541, 859428872, 698187080, + 1944954707, + ], + ) + }) + } + + pub fn from_big_int(i: &BigInt) -> Self { + // this would be likely slightly more efficient with rem_euclid, but + // it's only added in num-bigint 0.4, and we're stuck with 0.3 for now + // because of the Kernel SDK + let norm = i.modpow(&BigInt::one(), Self::order()); + let mut buf = [0u8; Self::BYTE_SIZE]; + let bytes_le = norm.to_biguint().unwrap().to_bytes_le(); + buf[0..bytes_le.len()].copy_from_slice(&bytes_le); + Self::from_bytes(&buf).unwrap() + } + + pub fn to_big_int(&self) -> BigInt { + BigInt::from_bytes_le(Sign::Plus, &self.to_bytes()) + } + + pub fn zero() -> Self { + Self::from_bytes(&[]).unwrap() + } + + pub fn one() -> Self { + Self::from_bytes(&[1]).unwrap() + } +} + +impl Add for &Fr { + type Output = Fr; + fn add(self, rhs: Self) -> Self::Output { + let res = unsafe { + let mut res = MaybeUninit::uninit(); + blst_fr_add(res.as_mut_ptr(), &self.0, &rhs.0); + res.assume_init() + }; + Fr(res) + } +} + +super::instances::instances!(Add, add, Fr, Fr); + +impl Mul for &Fr { + type Output = Fr; + fn mul(self, rhs: Self) -> Self::Output { + let res = unsafe { + let mut res = MaybeUninit::uninit(); + blst_fr_mul(res.as_mut_ptr(), &self.0, &rhs.0); + res.assume_init() + }; + Fr(res) + } +} + +super::instances::instances!(Mul, mul, Fr, Fr); + +impl Neg for &Fr { + type Output = Fr; + fn neg(self) -> Self::Output { + let mut res = blst_fr::default(); + unsafe { blst_fr_cneg(&mut res, &self.0, true) }; + Fr(res) + } +} + +impl Neg for Fr { + type Output = Fr; + fn neg(self) -> Self::Output { + (&self).neg() + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + + #[test] + fn order() { + assert_eq!( + Fr::order(), + &BigInt::from_str( + "52435875175126190479447740508185965837690552500527637822603658699938581184513" + ) + .unwrap() + ); + } + + #[test] + fn fr_from_to_bytes() { + // roundtrip tests from-to bytes + assert_eq!(&Fr::from_bytes(&[]).unwrap().to_bytes(), &[0; 32]); + assert_eq!( + &Fr::from_bytes(&[1]).unwrap().to_bytes(), + [&[1u8] as &[u8], &[0; 31]].concat().as_slice() + ); + let hex = &hex::decode("6582983f01c028b8959d31d2d5e537dd7ca38eaf8490c269727b2e5bd3ea961a") + .unwrap(); + assert_eq!(&Fr::from_bytes(hex).unwrap().to_bytes(), hex.as_slice()); + } + + #[test] + fn fr_from_big_int() { + // roundtrip tests from int to bytes + assert_eq!(&Fr::from_big_int(&0.into()).to_bytes(), &[0; 32]); + assert_eq!( + &Fr::from_big_int(&1.into()).to_bytes(), + [&[1u8] as &[u8], &[0; 31]].concat().as_slice() + ); + let int = BigInt::from_str(&"42".repeat(72)).unwrap(); + assert_eq!( + &Fr::from_big_int(&int).to_bytes(), + hex::decode("992529bdf61eb9e4a480fff193adb5e4a78ab7820d308f5582c015c2ded0103e") + .unwrap() + .as_slice() + ); + let neg_int = -BigInt::from_str(&"42".repeat(72)).unwrap(); + assert_eq!( + &Fr::from_big_int(&neg_int).to_bytes(), + hex::decode("68dad64208e1461b5adbfe0d6ff6076f5d4dea86faa7aaddc5bc876774d6dc35") + .unwrap() + .as_slice() + ); + assert_eq!( + &Fr::from_big_int(&(-1).into()), + &Fr::from_bytes( + &hex::decode("00000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73") + .unwrap() + ) + .unwrap(), + ) + } + + #[test] + fn fr_to_big_int() { + // roundtrip tests from bytes to int + assert_eq!(Fr::from_bytes(&[0; 32]).unwrap().to_big_int(), 0.into()); + assert_eq!(Fr::from_bytes(&[1]).unwrap().to_big_int(), 1.into()); + let int = BigInt::from_str(&"42".repeat(72)) + .unwrap() + .modpow(&1.into(), Fr::order()); + assert_eq!( + Fr::from_bytes( + &hex::decode("992529bdf61eb9e4a480fff193adb5e4a78ab7820d308f5582c015c2ded0103e") + .unwrap() + ) + .unwrap() + .to_big_int(), + int + ); + let neg_int = -BigInt::from_str(&"42".repeat(72)).unwrap(); + let neg_int_mod = neg_int.modpow(&BigInt::one(), Fr::order()); + assert_eq!( + Fr::from_bytes( + &hex::decode("68dad64208e1461b5adbfe0d6ff6076f5d4dea86faa7aaddc5bc876774d6dc35") + .unwrap() + ) + .unwrap() + .to_big_int(), + neg_int_mod, + ); + assert_eq!( + Fr::from_bytes( + &hex::decode("00000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73") + .unwrap() + ) + .unwrap() + .to_big_int(), + Fr::order() - BigInt::from(1), + ) + } + + #[test] + fn fr_add() { + let zero = &Fr::from_big_int(&0.into()); + let one = &Fr::from_big_int(&1.into()); + let two = &Fr::from_big_int(&2.into()); + let big = &Fr::from_big_int(&BigInt::from_str(&"42".repeat(72)).unwrap()); + let big1 = &Fr::from_bytes( + &hex::decode("9a2529bdf61eb9e4a480fff193adb5e4a78ab7820d308f5582c015c2ded0103e") + .unwrap(), + ) + .unwrap(); + let big2 = &Fr::from_bytes( + &hex::decode("314b527aee3d72c94aa500e424b7ad754a3dcdfb1288e477bc038e5a6afa3308") + .unwrap(), + ) + .unwrap(); + + assert_eq!(zero + one, *one); + assert_eq!(one + zero, *one); + assert_eq!(zero + zero, *zero); + assert_eq!(one + one, *two); + assert_eq!(big + zero, *big); + assert_eq!(zero + big, *big); + assert_eq!(big + one, *big1); + assert_eq!(one + big, *big1); + assert_eq!(big + big, *big2); + } + + #[test] + fn fr_mul() { + let zero = &Fr::from_big_int(&0.into()); + let one = &Fr::from_big_int(&1.into()); + let big = &Fr::from_big_int(&BigInt::from_str(&"42".repeat(72)).unwrap()); + let big2 = &Fr::from_bytes( + &hex::decode("cb6a743dd190f86475268c95979c93e8b6b84b3bf10754822c8eecc4d9ae281c") + .unwrap(), + ) + .unwrap(); + + assert_eq!(zero * one, *zero); + assert_eq!(one * zero, *zero); + assert_eq!(zero * zero, *zero); + assert_eq!(one * one, *one); + assert_eq!(big * zero, *zero); + assert_eq!(zero * big, *zero); + assert_eq!(big * one, *big); + assert_eq!(one * big, *big); + assert_eq!(big * big, *big2); + } + + #[test] + fn fr_neg() { + let zero = &Fr::from_big_int(&0.into()); + let one = &Fr::from_big_int(&1.into()); + let neg_one = &Fr::from_big_int(&(-1).into()); + let big = &Fr::from_big_int(&BigInt::from_str(&"42".repeat(72)).unwrap()); + let neg_big = &Fr::from_bytes( + &hex::decode("68dad64208e1461b5adbfe0d6ff6076f5d4dea86faa7aaddc5bc876774d6dc35") + .unwrap(), + ) + .unwrap(); + + assert_eq!(-zero, *zero); + assert_eq!(-one, *neg_one); + assert_eq!(-neg_one, *one); + assert_eq!(-big, *neg_big); + assert_eq!(-neg_big, *big); + } +} diff --git a/contrib/mir/src/bls/g1.rs b/contrib/mir/src/bls/g1.rs new file mode 100644 index 000000000000..0100e79a4c77 --- /dev/null +++ b/contrib/mir/src/bls/g1.rs @@ -0,0 +1,219 @@ +/******************************************************************************/ +/* */ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) [2023] Serokell */ +/* */ +/******************************************************************************/ + +use blst::*; +use std::{ + mem::MaybeUninit, + ops::{Add, Mul, Neg}, +}; + +use super::fr::Fr; + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct G1(blst_p1); + +impl G1 { + const BYTE_SIZE: usize = 96; + + pub fn from_bytes(bs: &[u8]) -> Option { + if bs.len() != Self::BYTE_SIZE { + None + } else { + // this could've been `MaybeUninit`, but `blst_p1_deserialize` may + // not necessarily initialize it, and conversely, it may be + // initialized even if `res` isn't `BLST_SUCCESS`. So we just + // initialize it a priori. + let mut affine = blst_p1_affine::default(); + let res = unsafe { blst_p1_deserialize(&mut affine, bs.as_ptr()) }; + if let BLST_ERROR::BLST_SUCCESS = res { + Self::from_affine(affine) + } else { + None + } + } + } + + fn from_affine(affine: blst_p1_affine) -> Option { + let p1 = unsafe { + let mut p1 = MaybeUninit::uninit(); + blst_p1_from_affine(p1.as_mut_ptr(), &affine); + p1.assume_init() + }; + if unsafe { blst_p1_in_g1(&p1) } { + Some(G1(p1)) + } else { + None + } + } + + pub(super) fn to_affine(&self) -> blst_p1_affine { + let mut affine = MaybeUninit::uninit(); + unsafe { + blst_p1_to_affine(affine.as_mut_ptr(), &self.0); + affine.assume_init() + } + } + + pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] { + let mut out = [0; Self::BYTE_SIZE]; + unsafe { + blst_p1_serialize(out.as_mut_ptr(), &self.0); + } + out + } + + pub fn zero() -> Self { + G1::from_bytes(&[ + 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + .unwrap() + } + + pub fn one() -> Self { + Self::from_affine(unsafe { blst::BLS12_381_G1 }).unwrap() + } + + pub fn neg_one() -> Self { + Self::from_affine(unsafe { blst::BLS12_381_NEG_G1 }).unwrap() + } +} + +impl Add for &G1 { + type Output = G1; + fn add(self, rhs: Self) -> Self::Output { + let res = unsafe { + let mut res = MaybeUninit::uninit(); + blst_p1_add_or_double(res.as_mut_ptr(), &self.0, &rhs.0); + res.assume_init() + }; + G1(res) + } +} + +super::instances::instances!(Add, add, G1, G1); + +impl Mul<&Fr> for &G1 { + type Output = G1; + fn mul(self, rhs: &Fr) -> Self::Output { + let bytes = rhs.to_bytes(); + let res = unsafe { + let mut res = MaybeUninit::uninit(); + blst_p1_mult(res.as_mut_ptr(), &self.0, bytes.as_ptr(), Fr::BYTE_SIZE * 8); + res.assume_init() + }; + G1(res) + } +} + +super::instances::instances!(Mul, mul, G1, Fr); + +impl Neg for &G1 { + type Output = G1; + fn neg(self) -> Self::Output { + self.clone().neg() + } +} + +impl Neg for G1 { + type Output = G1; + fn neg(mut self) -> Self::Output { + unsafe { blst_p1_cneg(&mut self.0, true) }; + self + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use num_bigint::BigInt; + + use super::*; + + fn two() -> G1 { + let s = "0572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28"; + G1::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + fn some_val() -> G1 { + let s = "026fcea34d1a4c5125142dfa3b616086309cab49e60e548d95de658af4d9329c269dc132bd5d884617e8767600daeee90c6f5d25f3d63540f3b799d291e5df4a90244346ed780d5c9d3afa8f3c9a196e089fa4edc4a9806592e8561d626579e3"; + G1::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + fn some_val_plus_one() -> G1 { + let s = "0c267c85b0895eab7a63d9d8c0aa9744f1d9eccdc95b8a38c32fe54b49ee71f901372d2e4f157a1298a7cfa907edbc3c1797fdc61d20c9428567c864bf1572d3b26cd98478111e8bd097ac929d9a80189b51e06d69c0f2185f1fed6bd9f18d84"; + G1::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + fn neg_some_val() -> G1 { + let s = "026fcea34d1a4c5125142dfa3b616086309cab49e60e548d95de658af4d9329c269dc132bd5d884617e8767600daeee90d91b4c445a9b15957640de3b165cd8cd453083e060d0562c9f5d811ba16dcb6160c5b10ecaa7f9a2716a9e29d9a30c8"; + G1::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + #[test] + fn from_to_bytes() { + // roundtrip tests from-to bytes + assert_eq!(G1::from_bytes(&G1::zero().to_bytes()).unwrap(), G1::zero()); + assert_eq!(G1::from_bytes(&G1::one().to_bytes()).unwrap(), G1::one()); + assert_eq!( + G1::from_bytes(&G1::neg_one().to_bytes()).unwrap(), + G1::neg_one() + ); + assert_eq!(G1::from_bytes(&some_val().to_bytes()).unwrap(), some_val()); + } + + #[test] + fn add() { + assert_eq!(G1::zero() + G1::zero(), G1::zero()); + assert_eq!(G1::zero() + G1::one(), G1::one()); + assert_eq!(G1::one() + G1::zero(), G1::one()); + assert_eq!(G1::one() + G1::neg_one(), G1::zero()); + assert_eq!(G1::one() + G1::one(), two()); + assert_eq!(G1::zero() + some_val(), some_val()); + assert_eq!(some_val() + G1::zero(), some_val()); + assert_eq!(G1::one() + some_val(), some_val_plus_one()); + assert_eq!(some_val() + G1::one(), some_val_plus_one()); + } + + #[test] + fn mul() { + let fr_zero = &Fr::from_big_int(&0.into()); + let fr_one = &Fr::from_big_int(&1.into()); + let fr_big = &Fr::from_big_int(&BigInt::from_str(&"42".repeat(72)).unwrap()); + + let g1_big = "15cd0704c8a681ffe8a47662dba6c04d1b0a560f133a60c6a81c0eb804d5453b14992bc3d1f01081d3ed3cf1362994b217e00dbcfdb7ec7cb455ed8381aa469f4d555b50fed7f5579fe4559cd5caf6fc9a07da522c7db5973168d1c74946dc51"; + let some_x_big = "0e76570914f9a1f625f3c97e389b5339f18b73fc22a291b5f88f2194fb665219ba96610fb573090527552a00ba78322a1393fdf1144cf64b35003b0ea4db6ce94b9bdac689634c94f29863836646a5f78285390c5e4101b83970546fd12d5d6e"; + + assert_eq!(G1::zero() * fr_one, G1::zero()); + assert_eq!(G1::zero() * fr_zero, G1::zero()); + assert_eq!(G1::zero() * fr_big, G1::zero()); + assert_eq!(G1::one() * fr_zero, G1::zero()); + assert_eq!(G1::one() * fr_one, G1::one()); + assert_eq!( + G1::one() * fr_big, + G1::from_bytes(&hex::decode(g1_big).unwrap()).unwrap() + ); + assert_eq!(some_val() * fr_zero, G1::zero()); + assert_eq!(some_val() * fr_one, some_val()); + assert_eq!( + some_val() * fr_big, + G1::from_bytes(&hex::decode(some_x_big).unwrap()).unwrap() + ); + } + + #[test] + fn neg() { + assert_eq!(-G1::zero(), G1::zero()); + assert_eq!(-G1::one(), G1::neg_one()); + assert_eq!(-G1::neg_one(), G1::one()); + assert_eq!(-some_val(), neg_some_val()); + assert_eq!(-neg_some_val(), some_val()); + } +} diff --git a/contrib/mir/src/bls/g2.rs b/contrib/mir/src/bls/g2.rs new file mode 100644 index 000000000000..4aa4bf349fc4 --- /dev/null +++ b/contrib/mir/src/bls/g2.rs @@ -0,0 +1,222 @@ +/******************************************************************************/ +/* */ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) [2023] Serokell */ +/* */ +/******************************************************************************/ + +use blst::*; +use std::{ + mem::MaybeUninit, + ops::{Add, Mul, Neg}, +}; + +use super::fr::Fr; + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct G2(blst_p2); + +impl G2 { + const BYTE_SIZE: usize = 192; + + pub fn from_bytes(bs: &[u8]) -> Option { + if bs.len() != Self::BYTE_SIZE { + None + } else { + // this could've been `MaybeUninit`, but `blst_p2_deserialize` may + // not necessarily initialize it, and conversely, it may be + // initialized even if `res` isn't `BLST_SUCCESS`. So we just + // initialize it a priori. + let mut affine = blst_p2_affine::default(); + let res = unsafe { blst_p2_deserialize(&mut affine, bs.as_ptr()) }; + if let BLST_ERROR::BLST_SUCCESS = res { + Self::from_affine(affine) + } else { + None + } + } + } + + fn from_affine(affine: blst_p2_affine) -> Option { + let p2 = unsafe { + let mut p2 = MaybeUninit::uninit(); + blst_p2_from_affine(p2.as_mut_ptr(), &affine); + p2.assume_init() + }; + if unsafe { blst_p2_in_g2(&p2) } { + Some(G2(p2)) + } else { + None + } + } + + pub(super) fn to_affine(&self) -> blst_p2_affine { + let mut affine = MaybeUninit::uninit(); + unsafe { + blst_p2_to_affine(affine.as_mut_ptr(), &self.0); + affine.assume_init() + } + } + + pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] { + let mut out = [0; Self::BYTE_SIZE]; + unsafe { + blst_p2_serialize(out.as_mut_ptr(), &self.0); + } + out + } + + pub fn zero() -> Self { + G2::from_bytes(&[ + 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + .unwrap() + } + + pub fn one() -> Self { + Self::from_affine(unsafe { blst::BLS12_381_G2 }).unwrap() + } + + pub fn neg_one() -> Self { + Self::from_affine(unsafe { blst::BLS12_381_NEG_G2 }).unwrap() + } +} + +impl Add for &G2 { + type Output = G2; + fn add(self, rhs: Self) -> Self::Output { + let res = unsafe { + let mut res = MaybeUninit::uninit(); + blst_p2_add_or_double(res.as_mut_ptr(), &self.0, &rhs.0); + res.assume_init() + }; + G2(res) + } +} + +super::instances::instances!(Add, add, G2, G2); + +impl Mul<&Fr> for &G2 { + type Output = G2; + fn mul(self, rhs: &Fr) -> Self::Output { + let bytes = rhs.to_bytes(); + let res = unsafe { + let mut res = MaybeUninit::uninit(); + blst_p2_mult(res.as_mut_ptr(), &self.0, bytes.as_ptr(), Fr::BYTE_SIZE * 8); + res.assume_init() + }; + G2(res) + } +} + +super::instances::instances!(Mul, mul, G2, Fr); + +impl Neg for &G2 { + type Output = G2; + fn neg(self) -> Self::Output { + self.clone().neg() + } +} + +impl Neg for G2 { + type Output = G2; + fn neg(mut self) -> Self::Output { + unsafe { blst_p2_cneg(&mut self.0, true) }; + self + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use num_bigint::BigInt; + + use super::*; + + fn two() -> G2 { + let s = "0a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a0530f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf30468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899"; + G2::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + fn some_val() -> G2 { + let s = "14e9b22683a66543ec447b7aa76e4404424709728507581d0b3f60a8062c3f7c7d3365197c59f7c961fa9731084f5be60d0a936e93d556bdef2032cdcae2fa9902dcbe105e01d7ab7126d83486d882c4efd2fc1ac55044157333be19acf0cb7a10bc41c8081c9babd8d5b41b645badd4a679b3d4e1b3ea2c0e1f53b39c00b3889a40306c9b9ee2da5831e90148334d91016474d07e0f4e36d2d51b5ca11b633b9a940b9c126aebf4a2537c18fdc6967fb677824bfa902157e53cb499a021e57b"; + G2::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + fn some_val_plus_one() -> G2 { + let s = "013df716b3c482bddcc4bfe70b03d2b5e0d23d2334fbf4ed4e255e503ef5f33b957e32d14255aef3a52e123ec8e30c4f08b326c9c77bf265440900ee3d4564e445cdeeb2e264f0ef4f5d99fd6e2b0579089fd6dfad7c7c9ebb85080b5ef34b7f15e3dd2d528184f1e2eb1a00ec561db3d9493b75fe5c2016e012767758d8e0c06635282fbf89b7e8ae0348db49f9de4919782a0ccce2e05b3207c59a5e91fb813c1cf29cf6893ec2df1633ea1d9d8b956bbe43fb381c8cdd80368003750589e5"; + G2::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + fn neg_some_val() -> G2 { + let s = "14e9b22683a66543ec447b7aa76e4404424709728507581d0b3f60a8062c3f7c7d3365197c59f7c961fa9731084f5be60d0a936e93d556bdef2032cdcae2fa9902dcbe105e01d7ab7126d83486d882c4efd2fc1ac55044157333be19acf0cb7a0944d02231634aee7245f39adeefff02bdfd97b011d1289359117eed5ab0429b846bcf9215b51d2561cd16feb7cc5d1a189c9d19bb70986378468c59a230499bc9e33fe8e11a26cac4dd5687f8ea5fa468347db2b6c3dea7d4c24b665fddc530"; + G2::from_bytes(&hex::decode(s).unwrap()).unwrap() + } + + #[test] + fn from_to_bytes() { + // roundtrip tests from-to bytes + assert_eq!(G2::from_bytes(&G2::zero().to_bytes()).unwrap(), G2::zero()); + assert_eq!(G2::from_bytes(&G2::one().to_bytes()).unwrap(), G2::one()); + assert_eq!( + G2::from_bytes(&G2::neg_one().to_bytes()).unwrap(), + G2::neg_one() + ); + assert_eq!(G2::from_bytes(&some_val().to_bytes()).unwrap(), some_val()); + } + + #[test] + fn add() { + assert_eq!(G2::zero() + G2::zero(), G2::zero()); + assert_eq!(G2::zero() + G2::one(), G2::one()); + assert_eq!(G2::one() + G2::zero(), G2::one()); + assert_eq!(G2::one() + G2::neg_one(), G2::zero()); + assert_eq!(G2::one() + G2::one(), two()); + assert_eq!(G2::zero() + some_val(), some_val()); + assert_eq!(some_val() + G2::zero(), some_val()); + assert_eq!(G2::one() + some_val(), some_val_plus_one()); + assert_eq!(some_val() + G2::one(), some_val_plus_one()); + } + + #[test] + fn mul() { + let fr_zero = &Fr::from_big_int(&0.into()); + let fr_one = &Fr::from_big_int(&1.into()); + let fr_big = &Fr::from_big_int(&BigInt::from_str(&"42".repeat(72)).unwrap()); + + let g2_big = "0b5bbe13765b651ac1181215aa1a0969066aba3d39901ca10db654062b4076c356de42cefd2984da91682ef163bf5ef30b1654b0e20cbca44d770acd316e3f5acf3a72af4bad5c7f049f6b1941c5e8436f4262a533d3a093c87837b0ceef17ef114485907e511668365e43b4c109ab001405cc50db4b73c22fb348222a1b42c1bb448662500436ec1c9c479f2b68702507aa21abf497dd885b657f99b61a5ffb06326102cac8feb88d0c0ea512eb03ecdd4f53fb42d481c51564244e664904ad"; + let some_x_big = "045e5ecb13336a379270d71fad62ed5456a2a304217b8f0cbf1d7227d61171608164f5f417b80882188e7e76c71ff26e05d226933e9ecfa709234599fa68826f13ee15fa2df01d0829a3bceb4c69a66595bb100f87866296396e5db0a1b80d5b0ed456304f5737d6e58a23eca930d9b96584e6be8106892799a03c061fecfec49861556f95d7ef41545bef191acded56154967175cf4071d281991ce5af32e501c5a0e979dc7c0713be506dd0c3f45dba7668eb8dc42ad26a87deb6300dc0199"; + + assert_eq!(G2::zero() * fr_one, G2::zero()); + assert_eq!(G2::zero() * fr_zero, G2::zero()); + assert_eq!(G2::zero() * fr_big, G2::zero()); + assert_eq!(G2::one() * fr_zero, G2::zero()); + assert_eq!(G2::one() * fr_one, G2::one()); + assert_eq!( + G2::one() * fr_big, + G2::from_bytes(&hex::decode(g2_big).unwrap()).unwrap() + ); + assert_eq!(some_val() * fr_zero, G2::zero()); + assert_eq!(some_val() * fr_one, some_val()); + assert_eq!( + some_val() * fr_big, + G2::from_bytes(&hex::decode(some_x_big).unwrap()).unwrap() + ); + } + + #[test] + fn neg() { + assert_eq!(-G2::zero(), G2::zero()); + assert_eq!(-G2::one(), G2::neg_one()); + assert_eq!(-G2::neg_one(), G2::one()); + assert_eq!(-some_val(), neg_some_val()); + assert_eq!(-neg_some_val(), some_val()); + } +} diff --git a/contrib/mir/src/bls/instances.rs b/contrib/mir/src/bls/instances.rs new file mode 100644 index 000000000000..8480edb321a5 --- /dev/null +++ b/contrib/mir/src/bls/instances.rs @@ -0,0 +1,36 @@ +/******************************************************************************/ +/* */ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) [2023] Serokell */ +/* */ +/******************************************************************************/ + +/// Helper macro to define binary arithmetic instances for reference +/// combinations, assuming an instance for the case when both are references +/// exists. +macro_rules! instances { + ($name:ident, $fn_name:ident, $lhs:ty, $rhs:ty) => { + impl $name<&$rhs> for $lhs { + type Output = <&'static $lhs as $name<&'static $rhs>>::Output; + fn $fn_name(self, rhs: &$rhs) -> Self::Output { + (&self).$fn_name(rhs) + } + } + + impl $name<$rhs> for &$lhs { + type Output = <&'static $lhs as $name<&'static $rhs>>::Output; + fn $fn_name(self, rhs: $rhs) -> Self::Output { + self.$fn_name(&rhs) + } + } + + impl $name<$rhs> for $lhs { + type Output = <&'static $lhs as $name<&'static $rhs>>::Output; + fn $fn_name(self, rhs: $rhs) -> Self::Output { + (&self).$fn_name(&rhs) + } + } + }; +} + +pub(super) use instances; diff --git a/contrib/mir/src/lib.rs b/contrib/mir/src/lib.rs index e7aae25728e8..3be6adcefe64 100644 --- a/contrib/mir/src/lib.rs +++ b/contrib/mir/src/lib.rs @@ -7,6 +7,7 @@ #![warn(clippy::redundant_clone)] pub mod ast; +pub mod bls; pub mod context; pub mod gas; pub mod interpreter; -- GitLab From 3d1a28d94de227f2e20eaa0544c863f57892af14 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 7 Dec 2023 19:50:20 +0300 Subject: [PATCH 3/3] MIR: typecheck Bls12381 values --- contrib/mir/src/ast.rs | 11 +- contrib/mir/src/ast/comparable.rs | 3 +- contrib/mir/src/gas.rs | 14 ++- contrib/mir/src/typechecker.rs | 172 +++++++++++++++++++++++++++++- 4 files changed, 196 insertions(+), 4 deletions(-) diff --git a/contrib/mir/src/ast.rs b/contrib/mir/src/ast.rs index 7fee222ba6e9..8c3f2f023e73 100644 --- a/contrib/mir/src/ast.rs +++ b/contrib/mir/src/ast.rs @@ -27,7 +27,10 @@ use std::{ pub use tezos_crypto_rs::hash::ChainId; use typed_arena::Arena; -use crate::{ast::annotations::NO_ANNS, lexer::Prim}; +use crate::{ + bls, + {ast::annotations::NO_ANNS, lexer::Prim}, +}; pub use byte_repr_trait::{ByteReprError, ByteReprTrait}; pub use micheline::IntoMicheline; @@ -266,6 +269,9 @@ pub enum TypedValue<'a> { Operation(Box>), Ticket(Box>), Timestamp(BigInt), + Bls12381Fr(bls::Fr), + Bls12381G1(bls::G1), + Bls12381G2(bls::G2), } impl<'a> IntoMicheline<'a> for TypedValue<'a> { @@ -307,6 +313,9 @@ impl<'a> IntoMicheline<'a> for TypedValue<'a> { TV::Lambda(lam) => lam.into_micheline_optimized_legacy(arena), TV::KeyHash(s) => V::Bytes(s.to_bytes_vec()), TV::Timestamp(s) => V::Int(s), + TV::Bls12381Fr(x) => V::Bytes(x.to_bytes().to_vec()), + TV::Bls12381G1(x) => V::Bytes(x.to_bytes().to_vec()), + TV::Bls12381G2(x) => V::Bytes(x.to_bytes().to_vec()), TV::Contract(x) => go(TV::Address(x)), TV::Operation(operation_info) => match operation_info.operation { Operation::TransferTokens(tt) => Micheline::App( diff --git a/contrib/mir/src/ast/comparable.rs b/contrib/mir/src/ast/comparable.rs index 5fa32c07749b..ee7feed83825 100644 --- a/contrib/mir/src/ast/comparable.rs +++ b/contrib/mir/src/ast/comparable.rs @@ -54,7 +54,8 @@ impl PartialOrd for TypedValue<'_> { // non-comparable types ( - List(..) | Set(..) | Map(..) | Contract(..) | Operation(_) | Ticket(_) | Lambda(..), + List(..) | Set(..) | Map(..) | Contract(..) | Operation(_) | Ticket(..) + | Lambda(..) | Bls12381Fr(..) | Bls12381G1(..) | Bls12381G2(..), _, ) => None, } diff --git a/contrib/mir/src/gas.rs b/contrib/mir/src/gas.rs index 1ec8bb9eca2a..f0241e2b2cf7 100644 --- a/contrib/mir/src/gas.rs +++ b/contrib/mir/src/gas.rs @@ -115,6 +115,15 @@ pub mod tc_cost { // `max(bls,ed25519,p256,secp256k1)`, which happens to be `bls` pub const KEY_HASH_OPTIMIZED: u32 = 80; + // corresponds to cost_DECODING_BLS_FR in the protocol. + pub const BLS_FR: u32 = 120; + + // corresponds to cost_DECODING_BLS_G1 in the protocol. + pub const BLS_G1: u32 = 54600; + + // corresponds to cost_DECODING_BLS_G2 in the protocol. + pub const BLS_G2: u32 = 69000; + // corresponds to cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_bls in the // protocol. the protocol computes cost as // `max(bls,ed25519,p256,secp256k1)`, which happens to be `bls` @@ -473,7 +482,10 @@ pub mod interpret_cost { | V::Contract(_) | V::Operation(_) | V::Ticket(_) - | V::Lambda(_), + | V::Lambda(_) + | V::Bls12381Fr(_) + | V::Bls12381G1(_) + | V::Bls12381G2(_), _, ) => incomparable(), }) diff --git a/contrib/mir/src/typechecker.rs b/contrib/mir/src/typechecker.rs index a2e0485cb937..2d586037d90e 100644 --- a/contrib/mir/src/typechecker.rs +++ b/contrib/mir/src/typechecker.rs @@ -23,13 +23,13 @@ use crate::ast::micheline::{ micheline_fields, micheline_instructions, micheline_literals, micheline_types, micheline_values, }; use crate::ast::michelson_address::AddressHash; -use crate::ast::*; use crate::context::Ctx; use crate::gas; use crate::gas::OutOfGas; use crate::irrefutable_match::irrefutable_match; use crate::lexer::Prim; use crate::stack::*; +use crate::{ast::*, bls}; /// Typechecker error type. #[derive(Debug, PartialEq, Eq, Clone, thiserror::Error)] @@ -1720,6 +1720,31 @@ pub(crate) fn typecheck_value<'a>( _ => return Err(TcError::InvalidValueForType(format!("{v:?}"), t.clone())), } } + (T::Bls12381Fr, V::Int(i)) => { + ctx.gas.consume(gas::tc_cost::BLS_FR)?; + TV::Bls12381Fr(bls::Fr::from_big_int(i)) + } + (T::Bls12381Fr, V::Bytes(bs)) => { + ctx.gas.consume(gas::tc_cost::BLS_FR)?; + TV::Bls12381Fr( + bls::Fr::from_bytes(bs) + .ok_or_else(|| TcError::InvalidValueForType(format!("{v:?}"), t.clone()))?, + ) + } + (T::Bls12381G1, V::Bytes(bs)) => { + ctx.gas.consume(gas::tc_cost::BLS_G1)?; + TV::Bls12381G1( + bls::G1::from_bytes(bs) + .ok_or_else(|| TcError::InvalidValueForType(format!("{v:?}"), t.clone()))?, + ) + } + (T::Bls12381G2, V::Bytes(bs)) => { + ctx.gas.consume(gas::tc_cost::BLS_G2)?; + TV::Bls12381G2( + bls::G2::from_bytes(bs) + .ok_or_else(|| TcError::InvalidValueForType(format!("{v:?}"), t.clone()))?, + ) + } (t, v) => return Err(TcError::InvalidValueForType(format!("{v:?}"), t.clone())), }) } @@ -2493,6 +2518,151 @@ mod typecheck_tests { assert_eq!(stack, tc_stk![Type::String]); } + #[test] + fn push_bls_fr_int() { + let mut stack = tc_stk![]; + assert_eq!( + typecheck_instruction( + &parse(r#"PUSH bls12_381_fr 100500"#).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Ok(Push(TypedValue::Bls12381Fr(bls::Fr::from_big_int( + &100500.into() + )))) + ); + assert_eq!(stack, tc_stk![Type::Bls12381Fr]); + } + + #[test] + fn push_bls_fr_hex() { + let mut stack = tc_stk![]; + assert_eq!( + typecheck_instruction( + &parse(r#"PUSH bls12_381_fr 0x01"#).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Ok(Push(TypedValue::Bls12381Fr( + bls::Fr::from_bytes(&[1]).unwrap() + ))) + ); + assert_eq!(stack, tc_stk![Type::Bls12381Fr]); + } + + #[test] + fn push_bls_fr_hex_too_long() { + let mut stack = tc_stk![]; + assert_eq!( + typecheck_instruction( + &parse(r#"PUSH bls12_381_fr 0x000000000000000000000000000000000000000000000000000000000000000000"#).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Err(TcError::InvalidValueForType("Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])".into(), Type::Bls12381Fr)) + ); + } + + #[test] + fn push_bls_g1() { + let mut stack = tc_stk![]; + let hex_val = "400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assert_eq!( + typecheck_instruction( + &parse(&format!("PUSH bls12_381_g1 0x{hex_val}")).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Ok(Push(TypedValue::Bls12381G1( + bls::G1::from_bytes(&hex::decode(hex_val).unwrap()).unwrap() + ))) + ); + } + + #[test] + fn push_bls_g1_short() { + let mut stack = tc_stk![]; + let hex_val = "40000000000000000000000000000000000000000000000000000000"; + assert_eq!( + typecheck_instruction( + &parse(&format!("PUSH bls12_381_g1 0x{hex_val}")).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Err(TcError::InvalidValueForType( + "Bytes([64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])".into(), + Type::Bls12381G1, + )) + ); + } + + #[test] + fn push_bls_g1_long() { + let mut stack = tc_stk![]; + let hex_val = "40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assert_eq!( + typecheck_instruction( + &parse(&format!("PUSH bls12_381_g1 0x{hex_val}")).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Err(TcError::InvalidValueForType( + "Bytes([64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])".into(), + Type::Bls12381G1, + )) + ); + } + + #[test] + fn push_bls_g2() { + let mut stack = tc_stk![]; + let hex_val = "400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assert_eq!( + typecheck_instruction( + &parse(&format!("PUSH bls12_381_g2 0x{hex_val}")).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Ok(Push(TypedValue::Bls12381G2( + bls::G2::from_bytes(&hex::decode(hex_val).unwrap()).unwrap() + ))) + ); + } + + #[test] + fn push_bls_g2_short() { + let mut stack = tc_stk![]; + let hex_val = "40000000000000000000000000000000000000000000000000000000"; + assert_eq!( + typecheck_instruction( + &parse(&format!("PUSH bls12_381_g2 0x{hex_val}")).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Err(TcError::InvalidValueForType( + "Bytes([64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])".into(), + Type::Bls12381G2, + )) + ); + } + + #[test] + fn push_bls_g2_long() { + let mut stack = tc_stk![]; + let hex_val = "40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assert_eq!( + typecheck_instruction( + &parse(&format!("PUSH bls12_381_g2 0x{hex_val}")).unwrap(), + &mut Ctx::default(), + &mut stack + ), + Err(TcError::InvalidValueForType( + "Bytes([64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])".into(), + Type::Bls12381G2, + )) + ); + } + #[test] fn push_unit_value() { let mut stack = tc_stk![]; -- GitLab