diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1948c70 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sw linguist-language=Rust diff --git a/.gitignore b/.gitignore index 088ba6b..2b898f5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,9 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +*/Cargo.lock +*/Forc.lock + +*/out/* +*/target/* \ No newline at end of file diff --git a/README.md b/README.md index 6639299..8b63b9f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ # fuel-crypto Various Cryptographic Primitives in Sway for the Fuel VM + +# Testing + +## Spin Up a Fuel node +From [here](https://fuellabs.github.io/sway/v0.19.0/introduction/overview.html). +In a separate tab in your terminal, spin up a local Fuel node: + + +`fuel-core --db-type in-memory` + +This starts a Fuel node with a volatile database that will be cleared when shut down (good for testing purposes). + + Make sure `fuel-core` is up to date. This can be done with [fuelup](https://github.com/FuelLabs/fuelup). Also, make sure there's only 1 `fuel-core` installed (check this with `which -a fuel-core`). + +## Edwards25519 + + The testing is done in Rust; the testfiles are in `/test_contract`. See reference in [Sway documentation](https://fuellabs.github.io/sway/v0.17.0/testing/testing-with-rust.html). + + In `main.sw` the contract makes the connection with the library code in edward25519. This in turn can be tested in the `/test_contract/tests` folder. + + To run tests for edwards25519 folder: + ``` + cd test_contract + forc test + ``` + +## BLS + + To run tests for bls folder: + ``` + cd tests-bls + forc test + ``` + +## Running a script +Also to run a script a Fuel Node has to be spun up. + +``` +forc run --unsigned --pretty-print +``` \ No newline at end of file diff --git a/bls12_381/.gitignore b/bls12_381/.gitignore new file mode 100644 index 0000000..77d3844 --- /dev/null +++ b/bls12_381/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml new file mode 100644 index 0000000..19f18a3 --- /dev/null +++ b/bls12_381/Cargo.toml @@ -0,0 +1,15 @@ +[project] +name = "bls12_381" +version = "0.1.0" +authors = ["Hashcloak"] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +fuels = { version = "0.23", features = ["fuel-core-lib"] } +tokio = { version = "1.12", features = ["rt", "macros"] } + +[[test]] +harness = true +name = "integration_tests" +path = "tests/harness.rs" diff --git a/bls12_381/Forc.toml b/bls12_381/Forc.toml new file mode 100644 index 0000000..ae041b9 --- /dev/null +++ b/bls12_381/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Hashcloak"] +entry = "lib.sw" +license = "Apache-2.0" +name = "bls12_381" + +[dependencies] diff --git a/bls12_381/src/choice.sw b/bls12_381/src/choice.sw new file mode 100644 index 0000000..54b889a --- /dev/null +++ b/bls12_381/src/choice.sw @@ -0,0 +1,204 @@ +library choice; + + +use core::num::*; +use std::{option::Option, u128::U128}; +use core::ops::{Eq, BitwiseAnd, BitwiseOr, BitwiseXor}; + +/////////////// IMPORTANT /////////////// + +// All of this is coming from the dalek cryptograhpy project +// see https://github.com/dalek-cryptography/subtle/blob/main/src/lib.rs + +/////////////// IMPORTANT /////////////// + +/// The `Choice` struct represents a choice for use in conditional assignment. +/// +/// It is a wrapper around a `u8`, which should have the value either `1` (true) +/// or `0` (false). +pub struct Choice { c: u8 } + +// Can't use name "From" because of collision with trait in U128 (even though not importing u128::*). +// This seems to be a bug in Sway, see discussion in Discord https://discord.com/channels/732892373507375164/734213700835082330/1007067117029433405 +pub trait From { + fn from(input: u8) -> Self; + fn into(self) -> u8; +} + +impl From for Choice { + fn from(input: u8) -> Self { + Choice { c: input } + } + + fn into(self) -> u8 { + self.c + } +} + +// If equals 1u8 => false, if 0u8 => true +pub fn opposite_choice_value(a: u8) -> bool { + asm(r1: a, r2) { + eq r2 r1 zero; + r2: bool + } +} + +impl Choice { + pub fn unwrap_u8(self) -> u8 { + self.c + } + + pub fn unwrap_as_bool(self) -> bool { + self.c == 1u8 + } + + pub fn from_bool(b: bool) -> Choice { + if b { + Choice{ c: 1u8} + } else { + Choice{ c: 0u8} + } + } +} + +impl Choice { + pub fn not(self) -> Choice { + ~Choice::from_bool(opposite_choice_value(self.c)) + } +} + +impl BitwiseXor for u8 { + fn binary_xor(self, other: Self) -> Self { + asm(r1: self, r2: other, r3) { + xor r3 r1 r2; + r3: u8 + } + } +} + +impl ConditionallySelectable for u8 { + fn conditional_select(a: u8, b: u8, choice: Choice) -> u32 { + let mask = wrapping_neg(choice.unwrap_u8()); + b.binary_xor(mask & (a.binary_xor(b))) + } +} + +impl ConditionallySelectable for Choice { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + ~Choice::from(~u8::conditional_select(a.c, b.c, choice)) + } +} + +impl BitwiseAnd for u8 { + fn binary_and(self, other: Self) -> Self { + asm(r1: self, r2: other, r3) { + and r3 r1 r2; + r3: u8 + } + } +} + +impl BitwiseAnd for Choice { + fn binary_and(self, other: Self) -> Self { + ~Choice::from(self.c & other.c) + } +} + +impl BitwiseOr for u8 { + fn binary_or(self, other: Self) -> Self { + asm(r1: self, r2: other, r3) { + or r3 r1 r2; + r3: u8 + } + } +} + +impl BitwiseOr for Choice { + fn binary_or(self, other: Self) -> Self { + ~Choice::from(self.c | other.c) + } +} + + +/// The `CtOption` type represents an optional value similar to the +/// [`Option`](core::option::Option) type but is intended for +/// use in constant time APIs. + +pub struct CtOption { + value: T, + is_some: Choice, +} + +impl CtOption { + /// This method is used to construct a new `CtOption` and takes + /// a value of type `T`, and a `Choice` that determines whether + /// the optional value should be `Some` or not. If `is_some` is + /// false, the value will still be stored but its value is never + /// exposed. + pub fn new(value: T, is_some: Choice) -> CtOption { + CtOption { + value: value, + is_some: is_some, + } + } + + pub fn new_from_bool(value: T, is_some: bool) -> CtOption { + match is_some { + true => CtOption {value: value, is_some: Choice{ c: 1 },}, + false => CtOption {value: value, is_some: Choice{ c: 0 },}, + } + } + + //To reference `is_some` this would have to go in a separate Impl + pub fn is_none(self) -> bool { + !self.is_some.unwrap_as_bool() + } + + pub fn is_some(self) -> bool { + self.is_some.unwrap_as_bool() + } + + pub fn unwrap(self) -> T { + self.value + } + + // unwrap_or can't be implemented here.. + // There is no type restriction possible on generics in Sway + // See https://discord.com/channels/732892373507375164/734213700835082330/1007097764242522273 +} + +pub trait ConditionallySelectable { + // Select a if choice == 1 or select b if choice == 0, in constant time. + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self; +} + +// From https://github.com/dalek-cryptography/subtle/blob/main/src/lib.rs +pub trait ConstantTimeEq { + fn ct_eq(self, other: Self) -> Choice; +} + +fn add_wrap_64(a: u64, b :u64) -> u64 { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + (a_128 + b_128).lower +} + +pub fn wrapping_neg(a: u64) -> u64 { + add_wrap_64(~u64::max() - a, 1) +} + +impl ConstantTimeEq for u64 { + fn ct_eq(self, other: u64) -> Choice { + // comments from reference impl + // x == 0 if and only if self == other + let x: u64 = self ^ other; + + // If x == 0, then x and -x are both equal to zero; + // otherwise, one or both will have its high bit set. + let y: u64 = (x | wrapping_neg(x)) >> 63; + + // Result is the opposite of the high bit (now shifted to low). + let res: u8 = y ^ (1u64); + ~Choice::from(res) + } +} \ No newline at end of file diff --git a/bls12_381/src/f12.sw b/bls12_381/src/f12.sw new file mode 100644 index 0000000..956c467 --- /dev/null +++ b/bls12_381/src/f12.sw @@ -0,0 +1,103 @@ +library fp12; + +dep fp6; + +use fp6::Fp6; +use choice::{ConstantTimeEq}; +use core::ops::{Eq, Add, Subtract, Multiply}; + +pub struct Fp12 { + c0: Fp6, + c1: Fp6, +} + +impl ConditionallySelectable for Fp12 { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + Fp12 { + c0: ~Fp6::conditional_select(a.c0, b.c0, choice), + c1: ~Fp6::conditional_select(a.c1, b.c1, choice), + } + } +} + +impl ConstantTimeEq for Fp12 { + fn ct_eq(self, other: Self) -> Choice { + self.c0.ct_eq(other.c0) & self.c1.ct_eq(other.c1) + } +} + +impl Fp12 { + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } + + pub fn zero() -> Self { + Fp12 { + c0: ~Fp6::zero(), + c1: ~Fp6::zero(), + } + } + + pub fn one() -> Self { + Fp12 { + c0: ~Fp6::one(), + c1: ~Fp6::zero(), + } + } + + fn from(f: Fp) -> Fp12 { + Fp12 { + c0: ~Fp6::from(f), + c1: ~Fp6::zero(), + } + } + + fn from(f: Fp2) -> Fp12 { + Fp12 { + c0: ~Fp6::from(f), + c1: ~Fp6::zero(), + } + } + + fn from(f: Fp6) -> Fp12 { + Fp12 { + c0: f, + c1: ~Fp6::zero(), + } + } + + pub fn is_zero(self) -> Choice { + self.c0.is_zero().binary_and(self.c1.is_zero()) + } + + fn neg(self) -> Self { + Fp12 { + c0: self.c0.neg(), + c1: self.c1.neg(), + } + } +} + +impl Eq for Fp12 { + fn eq(self, other: Self) -> bool { + self.eq(other) + } +} + +impl Add for Fp12 { + fn add(self, rhs: Fp12) -> Self { + Fp12 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + } + } +} + +impl Subtract for Fp12 { + fn sub(self, rhs: Fp12) -> Self { + Fp12 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + } + } +} \ No newline at end of file diff --git a/bls12_381/src/fp.sw b/bls12_381/src/fp.sw new file mode 100644 index 0000000..32fd532 --- /dev/null +++ b/bls12_381/src/fp.sw @@ -0,0 +1,556 @@ +library fp; + +dep choice; +dep util; + +//This import is needed because of importing ConstantTimeEq for u64 (since it's a trait for a primitive type) +use choice::*; +use util::*; +use std::{option::Option, u128::U128, vec::Vec}; +use core::ops::{Eq, Add, Subtract, Multiply}; + +// Little endian big integer with 6 limbs +// in Montgomery form +pub struct Fp{ls: [u64;6]} + +/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +pub const MODULUS: [u64; 6] = [ + 0xb9fe_ffff_ffff_aaab, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, +]; + +/// INV = -(P^{-1} mod 2^64) mod 2^64 +pub const INV: u64 = 0x89f3_fffc_fffc_fffd; + +/// R = 2^384 mod p +const R: Fp = Fp{ls: [ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, +]}; + +/// R2 = 2^(384*2) mod p +const R2: Fp = Fp{ls: [ + 0xf4df_1f34_1c34_1746, + 0x0a76_e6a6_09d1_04f1, + 0x8de5_476c_4c95_b6d5, + 0x67eb_88a9_939d_83c0, + 0x9a79_3e85_b519_952d, + 0x1198_8fe5_92ca_e3aa, +]}; + +/// R3 = 2^(384*3) mod p +const R3: Fp = Fp{ls: [ + 0xed48_ac6b_d94c_a1e0, + 0x315f_831e_03a7_adf8, + 0x9a53_352a_615e_29dd, + 0x34c0_4e5e_921e_1761, + 0x2512_d435_6572_4728, + 0x0aa6_3460_9175_5d4d, +]}; + +impl ConditionallySelectable for Fp { + fn conditional_select(a: Fp, b: Fp, choice: Choice) -> Fp { + Fp{ ls: [ + ~u64::conditional_select(a.ls[0], b.ls[0], choice), + ~u64::conditional_select(a.ls[1], b.ls[1], choice), + ~u64::conditional_select(a.ls[2], b.ls[2], choice), + ~u64::conditional_select(a.ls[3], b.ls[3], choice), + ~u64::conditional_select(a.ls[4], b.ls[4], choice), + ~u64::conditional_select(a.ls[5], b.ls[5], choice), + ]} + } +} + +fn not(input: u64) -> u64 { + ~u64::max() - input +} + +impl ConstantTimeEq for Fp { + fn ct_eq(self, other: Fp) -> Choice { + ~u64::ct_eq(self.ls[0], other.ls[0]) + .binary_and(~u64::ct_eq(self.ls[1], other.ls[1])) + .binary_and(~u64::ct_eq(self.ls[2], other.ls[2])) + .binary_and(~u64::ct_eq(self.ls[3], other.ls[3])) + .binary_and(~u64::ct_eq(self.ls[4], other.ls[4])) + .binary_and(~u64::ct_eq(self.ls[5], other.ls[5])) + } +} + +impl Fp { + pub fn zero() -> Fp { + Fp{ls: [0, 0, 0, 0, 0, 0]} + } + + pub fn one() -> Fp { + R + } + + // -a mod p + pub fn neg(self) -> Fp { + let (d0, borrow) = sbb(MODULUS[0], self.ls[0], 0); + let (d1, borrow) = sbb(MODULUS[1], self.ls[1], borrow); + let (d2, borrow) = sbb(MODULUS[2], self.ls[2], borrow); + let (d3, borrow) = sbb(MODULUS[3], self.ls[3], borrow); + let (d4, borrow) = sbb(MODULUS[4], self.ls[4], borrow); + let (d5, _) = sbb(MODULUS[5], self.ls[5], borrow); + + // The mask should be 0 when a==p and 2^65-1 otherwise + // limbs = 0 when self = 0 + let limbs = (self.ls[0] | self.ls[1] | self.ls[2] | self.ls[3] | self.ls[4] | self.ls[5]); + // p mod p = 0, so this checks whether self is p + // a_is_p = 0 when self = 0, otherwise a_is_p = 1 + let a_is_p: u64 = is_zero_u64(limbs); + // mask = a_is_p - 1. This will give either 1-1 (=0) or 0-1 (wrap around to 2^64-1) + let mask = subtract_1_wrap(a_is_p); + + Fp { + ls: [d0 & mask, d1 & mask, d2 & mask, d3 & mask, d4 & mask, d5 & mask] + } + } + + // If a >= p, return a-p, else return a + pub fn subtract_p(self) -> Fp { + let (r0, borrow) = sbb(self.ls[0], MODULUS[0], 0); + let (r1, borrow) = sbb(self.ls[1], MODULUS[1], borrow); + let (r2, borrow) = sbb(self.ls[2], MODULUS[2], borrow); + let (r3, borrow) = sbb(self.ls[3], MODULUS[3], borrow); + let (r4, borrow) = sbb(self.ls[4], MODULUS[4], borrow); + let (r5, borrow) = sbb(self.ls[5], MODULUS[5], borrow); + + // The final borrow is 0xfff...fff if there was underflow. Otherwise 0. + let mut mask = borrow; + let r0 = (self.ls[0] & mask) | (r0 & not(mask)); + let r1 = (self.ls[1] & mask) | (r1 & not(mask)); + let r2 = (self.ls[2] & mask) | (r2 & not(mask)); + let r3 = (self.ls[3] & mask) | (r3 & not(mask)); + let r4 = (self.ls[4] & mask) | (r4 & not(mask)); + let r5 = (self.ls[5] & mask) | (r5 & not(mask)); + + Fp { + ls: [r0, r1, r2, r3, r4, r5] + } + } +} + +pub fn from_raw_unchecked(v: [u64; 6]) -> Fp { + Fp{ ls: v } +} + +impl Fp { + pub fn is_zero(self) -> Choice { + self.ct_eq(~Fp::zero()) + } + + fn add(self, rhs: Fp) -> Fp { + let (d0, carry) = adc(self.ls[0], rhs.ls[0], 0); + let (d1, carry) = adc(self.ls[1], rhs.ls[1], carry); + let (d2, carry) = adc(self.ls[2], rhs.ls[2], carry); + let (d3, carry) = adc(self.ls[3], rhs.ls[3], carry); + let (d4, carry) = adc(self.ls[4], rhs.ls[4], carry); + let (d5, _) = adc(self.ls[5], rhs.ls[5], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (Fp{ls:[d0, d1, d2, d3, d4, d5]}).subtract_p() + } + + pub fn mul(self, rhs: Fp) -> Fp { + let self0 = self.ls[0]; + let self1 = self.ls[1]; + let self2 = self.ls[2]; + let self3 = self.ls[3]; + let self4 = self.ls[4]; + let self5 = self.ls[5]; + + let rhs0 = rhs.ls[0]; + let rhs1 = rhs.ls[1]; + let rhs2 = rhs.ls[2]; + let rhs3 = rhs.ls[3]; + let rhs4 = rhs.ls[4]; + let rhs5 = rhs.ls[5]; + + let (t0, carry) = mac(0, self0, rhs0, 0); + let (t1, carry) = mac(0, self0, rhs1, carry); + let (t2, carry) = mac(0, self0, rhs2, carry); + let (t3, carry) = mac(0, self0, rhs3, carry); + let (t4, carry) = mac(0, self0, rhs4, carry); + let (t5, t6) = mac(0, self0, rhs5, carry); + + let (t1, carry) = mac(t1, self1, rhs0, 0); + let (t2, carry) = mac(t2, self1, rhs1, carry); + let (t3, carry) = mac(t3, self1, rhs2, carry); + let (t4, carry) = mac(t4, self1, rhs3, carry); + let (t5, carry) = mac(t5, self1, rhs4, carry); + let (t6, t7) = mac(t6, self1, rhs5, carry); + + let (t2, carry) = mac(t2, self2, rhs0, 0); + let (t3, carry) = mac(t3, self2, rhs1, carry); + let (t4, carry) = mac(t4, self2, rhs2, carry); + let (t5, carry) = mac(t5, self2, rhs3, carry); + let (t6, carry) = mac(t6, self2, rhs4, carry); + let (t7, t8) = mac(t7, self2, rhs5, carry); + + let (t3, carry) = mac(t3, self3, rhs0, 0); + let (t4, carry) = mac(t4, self3, rhs1, carry); + let (t5, carry) = mac(t5, self3, rhs2, carry); + let (t6, carry) = mac(t6, self3, rhs3, carry); + let (t7, carry) = mac(t7, self3, rhs4, carry); + let (t8, t9) = mac(t8, self3, rhs5, carry); + + let (t4, carry) = mac(t4, self4, rhs0, 0); + let (t5, carry) = mac(t5, self4, rhs1, carry); + let (t6, carry) = mac(t6, self4, rhs2, carry); + let (t7, carry) = mac(t7, self4, rhs3, carry); + let (t8, carry) = mac(t8, self4, rhs4, carry); + let (t9, t10) = mac(t9, self4, rhs5, carry); + + let (t5, carry) = mac(t5, self5, rhs0, 0); + let (t6, carry) = mac(t6, self5, rhs1, carry); + let (t7, carry) = mac(t7, self5, rhs2, carry); + let (t8, carry) = mac(t8, self5, rhs3, carry); + let (t9, carry) = mac(t9, self5, rhs4, carry); + let (t10, t11) = mac(t10, self5, rhs5, carry); + + let res: [u64;12] = [t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11]; + montgomery_reduce(res) + } + + pub fn square(self) -> Fp { + let (t1, carry) = mac(0, self.ls[0], self.ls[1], 0); + let (t2, carry) = mac(0, self.ls[0], self.ls[2], carry); + let (t3, carry) = mac(0, self.ls[0], self.ls[3], carry); + let (t4, carry) = mac(0, self.ls[0], self.ls[4], carry); + let (t5, t6) = mac(0, self.ls[0], self.ls[5], carry); + + let (t3, carry) = mac(t3, self.ls[1], self.ls[2], 0); + let (t4, carry) = mac(t4, self.ls[1], self.ls[3], carry); + let (t5, carry) = mac(t5, self.ls[1], self.ls[4], carry); + let (t6, t7) = mac(t6, self.ls[1], self.ls[5], carry); + + let (t5, carry) = mac(t5, self.ls[2], self.ls[3], 0); + let (t6, carry) = mac(t6, self.ls[2], self.ls[4], carry); + let (t7, t8) = mac(t7, self.ls[2], self.ls[5], carry); + + let (t7, carry) = mac(t7, self.ls[3], self.ls[4], 0); + let (t8, t9) = mac(t8, self.ls[3], self.ls[5], carry); + + let (t9, t10) = mac(t9, self.ls[4], self.ls[5], 0); + + let t11 = t10 >> 63; + let t10 = (t10 << 1) | (t9 >> 63); + let t9 = (t9 << 1) | (t8 >> 63); + let t8 = (t8 << 1) | (t7 >> 63); + let t7 = (t7 << 1) | (t6 >> 63); + let t6 = (t6 << 1) | (t5 >> 63); + let t5 = (t5 << 1) | (t4 >> 63); + let t4 = (t4 << 1) | (t3 >> 63); + let t3 = (t3 << 1) | (t2 >> 63); + let t2 = (t2 << 1) | (t1 >> 63); + let t1 = t1 << 1; + + let (t0, carry) = mac(0, self.ls[0], self.ls[0], 0); + let (t1, carry) = adc(t1, 0, carry); + let (t2, carry) = mac(t2, self.ls[1], self.ls[1], carry); + let (t3, carry) = adc(t3, 0, carry); + let (t4, carry) = mac(t4, self.ls[2], self.ls[2], carry); + let (t5, carry) = adc(t5, 0, carry); + let (t6, carry) = mac(t6, self.ls[3], self.ls[3], carry); + let (t7, carry) = adc(t7, 0, carry); + let (t8, carry) = mac(t8, self.ls[4], self.ls[4], carry); + let (t9, carry) = adc(t9, 0, carry); + let (t10, carry) = mac(t10, self.ls[5], self.ls[5], carry); + let (t11, _) = adc(t11, 0, carry); + + let res: [u64;12] = [t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11]; + montgomery_reduce(res) + } + +} + +impl Fp { + fn sub(self, rhs: Fp) -> Fp { + (rhs.neg()).add(self) + } + +// TODO implement when performing *many* squares is possible + pub fn pow_vartime(self, by: [u64; 6]) -> Self { + // let mut res = Self::one(); + // for e in by.iter().rev() { + // for i in (0..64).rev() { + // res = res.square(); + + // if ((*e >> i) & 1) == 1 { + // res *= self; + // } + // } + // } + // res + ~Fp::zero() + } + + // In Rust this is implemented as sum_of_products for T, but this is not possible in Sway + // Since specifically T=2 and T=6 is used, we implement both of them separately + pub fn sum_of_products_6(a: [Fp; 6], b: [Fp; 6]) -> Fp { + let mut u1 = 0; + let mut u2 = 0; + let mut u3 = 0; + let mut u4 = 0; + let mut u5 = 0; + let mut u6 = 0; + + let mut j = 0; + + while j < 6 { + let mut t0 = u1; + let mut t1 = u2; + let mut t2 = u3; + let mut t3 = u4; + let mut t4 = u5; + let mut t5 = u6; + let mut t6 = 0; + + let mut i = 0; + while i < 6 { + let (t0_temp, carry) = mac(t0, a[i].ls[j], b[i].ls[0], 0); + let (t1_temp, carry) = mac(t1, a[i].ls[j], b[i].ls[1], carry); + let (t2_temp, carry) = mac(t2, a[i].ls[j], b[i].ls[2], carry); + let (t3_temp, carry) = mac(t3, a[i].ls[j], b[i].ls[3], carry); + let (t4_temp, carry) = mac(t4, a[i].ls[j], b[i].ls[4], carry); + let (t5_temp, carry) = mac(t5, a[i].ls[j], b[i].ls[5], carry); + let (t6_temp, _) = adc(t6, 0, carry); + // assigning directly to t0..t6 didn't work. + t0 = t0_temp; + t1 = t1_temp; + t2 = t2_temp; + t3 = t3_temp; + t4 = t4_temp; + t5 = t5_temp; + t6 = t6_temp; + i += 1; + } + + let k = wrapping_mul(t0, INV); + let (_, carry) = mac(t0, k, MODULUS[0], 0); + let (u1_temp, carry) = mac(t1, k, MODULUS[1], carry); + let (u2_temp, carry) = mac(t2, k, MODULUS[2], carry); + let (u3_temp, carry) = mac(t3, k, MODULUS[3], carry); + let (u4_temp, carry) = mac(t4, k, MODULUS[4], carry); + let (u5_temp, carry) = mac(t5, k, MODULUS[5], carry); + let (u6_temp, _) = adc(t6, 0, carry); + // assigning directly to u1..u6 didn't work. + u1 = u1_temp; + u2 = u2_temp; + u3 = u3_temp; + u4 = u4_temp; + u5 = u5_temp; + u6 = u6_temp; + j += 1; + } + + (Fp{ ls: [u1, u2, u3, u4, u5, u6]}).subtract_p() + } + + pub fn sum_of_products_2(a: [Fp; 2], b: [Fp; 2]) -> Fp { + let mut u1 = 0; + let mut u2 = 0; + let mut u3 = 0; + let mut u4 = 0; + let mut u5 = 0; + let mut u6 = 0; + + let mut j = 0; + + while j < 6 { + let mut t0 = u1; + let mut t1 = u2; + let mut t2 = u3; + let mut t3 = u4; + let mut t4 = u5; + let mut t5 = u6; + let mut t6 = 0; + + let mut i = 0; + while i < 2 { + let (t0_temp, carry) = mac(t0, a[i].ls[j], b[i].ls[0], 0); + let (t1_temp, carry) = mac(t1, a[i].ls[j], b[i].ls[1], carry); + let (t2_temp, carry) = mac(t2, a[i].ls[j], b[i].ls[2], carry); + let (t3_temp, carry) = mac(t3, a[i].ls[j], b[i].ls[3], carry); + let (t4_temp, carry) = mac(t4, a[i].ls[j], b[i].ls[4], carry); + let (t5_temp, carry) = mac(t5, a[i].ls[j], b[i].ls[5], carry); + let (t6_temp, _) = adc(t6, 0, carry); + // assigning directly to t0..t6 didn't work. + t0 = t0_temp; + t1 = t1_temp; + t2 = t2_temp; + t3 = t3_temp; + t4 = t4_temp; + t5 = t5_temp; + t6 = t6_temp; + i += 1; + } + + let k = wrapping_mul(t0, INV); + let (_, carry) = mac(t0, k, MODULUS[0], 0); + let (u1_temp, carry) = mac(t1, k, MODULUS[1], carry); + let (u2_temp, carry) = mac(t2, k, MODULUS[2], carry); + let (u3_temp, carry) = mac(t3, k, MODULUS[3], carry); + let (u4_temp, carry) = mac(t4, k, MODULUS[4], carry); + let (u5_temp, carry) = mac(t5, k, MODULUS[5], carry); + let (u6_temp, _) = adc(t6, 0, carry); + // assigning directly to u1..u6 didn't work. + u1 = u1_temp; + u2 = u2_temp; + u3 = u3_temp; + u4 = u4_temp; + u5 = u5_temp; + u6 = u6_temp; + j += 1; + } + + (Fp{ ls: [u1, u2, u3, u4, u5, u6]}).subtract_p() + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + pub fn lexicographically_largest(self) -> Choice { + // This can be determined by checking to see if the element is + // larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1 + // and there is no underflow, then the element must be larger than + // (p - 1) // 2. + + // First, because self is in Montgomery form we need to reduce it + let tmp = montgomery_reduce( + [self.ls[0], self.ls[1], self.ls[2], self.ls[3], self.ls[4], self.ls[5], 0, 0, 0, 0, 0, 0,] + ); + + let (_, borrow) = sbb(tmp.ls[0], 0xdcff_7fff_ffff_d556, 0); + let (_, borrow) = sbb(tmp.ls[1], 0x0f55_ffff_58a9_ffff, borrow); + let (_, borrow) = sbb(tmp.ls[2], 0xb398_6950_7b58_7b12, borrow); + let (_, borrow) = sbb(tmp.ls[3], 0xb23b_a5c2_79c2_895f, borrow); + let (_, borrow) = sbb(tmp.ls[4], 0x258d_d3db_21a5_d66b, borrow); + let (_, borrow) = sbb(tmp.ls[5], 0x0d00_88f5_1cbf_f34d, borrow); + + // If the element was smaller, the subtraction will underflow + // producing a borrow value of 0xffff...ffff, otherwise it will + // be zero. We create a Choice representing true if there was + // overflow (and so this element is not lexicographically larger + // than its negation) and then negate it. + + let borrow_u8: u8 = borrow; + ~Choice::from(borrow_u8 & 1).not() + } + +} + +impl Fp { + +//TODO pow_vartime has to be implemented + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(self) -> CtOption { + // Exponentiate by p - 2 + let t = self.pow_vartime([ + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, + ]); + + ~CtOption::new_from_bool(t, !self.is_zero().unwrap_as_bool()) + } +} + +// Eq in Sway requires bool return type +impl Eq for Fp { + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } +} + +impl Add for Fp { + fn add(self, other: Self) -> Self { + self.add(other) + } +} + +impl Subtract for Fp { + fn subtract(self, other: Self) -> Self { + self.sub(other) + } +} + +impl Multiply for Fp { + fn multiply(self, other: Self) -> Self { + self.mul(other) + } +} + +pub fn montgomery_reduce(t: [u64;12]) -> Fp { + let k = wrapping_mul(t[0], INV); + + let r0: (u64, u64) = mac(t[0], k, MODULUS[0], 0); + let r1: (u64, u64) = mac(t[1], k, MODULUS[1], r0.1); + let r2: (u64, u64) = mac(t[2], k, MODULUS[2], r1.1); + let r3: (u64, u64) = mac(t[3], k, MODULUS[3], r2.1); + let r4: (u64, u64) = mac(t[4], k, MODULUS[4], r3.1); + let r5: (u64, u64) = mac(t[5], k, MODULUS[5], r4.1); + let r6_7: (u64, u64) = adc(t[6], 0, r5.1); + + let k = wrapping_mul(r1.0, INV); + let r0: (u64, u64) = mac(r1.0, k, MODULUS[0], 0); + let r2: (u64, u64) = mac(r2.0, k, MODULUS[1], r0.1); + let r3: (u64, u64) = mac(r3.0, k, MODULUS[2], r2.1); + let r4: (u64, u64) = mac(r4.0, k, MODULUS[3], r3.1); + let r5: (u64, u64) = mac(r5.0, k, MODULUS[4], r4.1); + let r6: (u64, u64) = mac(r6_7.0, k, MODULUS[5], r5.1); + let r7_8: (u64, u64) = adc(t[7], r6_7.1, r6.1); + + let k = wrapping_mul(r2.0, INV); + let r0: (u64, u64) = mac(r2.0, k, MODULUS[0], 0); + let r3: (u64, u64) = mac(r3.0, k, MODULUS[1], r0.1); + let r4: (u64, u64) = mac(r4.0, k, MODULUS[2], r3.1); + let r5: (u64, u64) = mac(r5.0, k, MODULUS[3], r4.1); + let r6: (u64, u64) = mac(r6.0, k, MODULUS[4], r5.1); + let r7: (u64, u64) = mac(r7_8.0, k, MODULUS[5], r6.1); + let r8_9: (u64, u64) = adc(t[8], r7_8.1, r7.1); + + let k = wrapping_mul(r3.0, INV); + let r0: (u64, u64) = mac(r3.0, k, MODULUS[0], 0); + let r4: (u64, u64) = mac(r4.0, k, MODULUS[1], r0.1); + let r5: (u64, u64) = mac(r5.0, k, MODULUS[2], r4.1); + let r6: (u64, u64) = mac(r6.0, k, MODULUS[3], r5.1); + let r7: (u64, u64) = mac(r7.0, k, MODULUS[4], r6.1); + let r8: (u64, u64) = mac(r8_9.0, k, MODULUS[5], r7.1); + let r9_10: (u64, u64) = adc(t[9], r8_9.1, r8.1); + + let k = wrapping_mul(r4.0, INV); + let r0: (u64, u64) = mac(r4.0, k, MODULUS[0], 0); + let r5: (u64, u64) = mac(r5.0, k, MODULUS[1], r0.1); + let r6: (u64, u64) = mac(r6.0, k, MODULUS[2], r5.1); + let r7: (u64, u64) = mac(r7.0, k, MODULUS[3], r6.1); + let r8: (u64, u64) = mac(r8.0, k, MODULUS[4], r7.1); + let r9: (u64, u64) = mac(r9_10.0, k, MODULUS[5], r8.1); + let r10_11: (u64, u64) = adc(t[10], r9_10.1, r9.1); + + let k = wrapping_mul(r5.0, INV); + let r0: (u64, u64) = mac(r5.0, k, MODULUS[0], 0); + let r6: (u64, u64) = mac(r6.0, k, MODULUS[1], r0.1); + let r7: (u64, u64) = mac(r7.0, k, MODULUS[2], r6.1); + let r8: (u64, u64) = mac(r8.0, k, MODULUS[3], r7.1); + let r9: (u64, u64) = mac(r9.0, k, MODULUS[4], r8.1); + let r10: (u64, u64) = mac(r10_11.0, k, MODULUS[5], r9.1); + let r11_12 = adc(t[11], r10_11.1, r10.1); + + (Fp { ls: [r6.0, r7.0, r8.0, r9.0, r10.0, r11_12.0]}).subtract_p() +} \ No newline at end of file diff --git a/bls12_381/src/fp2.sw b/bls12_381/src/fp2.sw new file mode 100644 index 0000000..9ad2ebd --- /dev/null +++ b/bls12_381/src/fp2.sw @@ -0,0 +1,183 @@ +library fp2; + +dep fp; +dep choice; + +use fp::Fp; +use core::ops::{Eq, Add, Subtract, Multiply}; +use choice::*; + +pub struct Fp2 { + c0: Fp, + c1: Fp, +} + +impl ConstantTimeEq for Fp2 { + fn ct_eq(self, other: Self) -> Choice { + self.c0.ct_eq(other.c0) & self.c1.ct_eq(other.c1) + } +} + +impl ConditionallySelectable for Fp2 { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + Fp2 { + c0: ~Fp::conditional_select(a.c0, b.c0, choice), + c1: ~Fp::conditional_select(a.c1, b.c1, choice), + } + } +} + +impl Fp2 { + fn from(f: Fp) -> Fp2 { + Fp2 { + c0: f, + c1: ~Fp::zero(), + } + } + + pub fn zero() -> Fp2 { + Fp2 { + c0: ~Fp::zero(), + c1: ~Fp::zero(), + } + } + + pub fn one() -> Fp2 { + Fp2 { + c0: ~Fp::one(), + c1: ~Fp::zero(), + } + } + + pub fn is_zero(self) -> Choice { + self.c0.is_zero().binary_and(self.c1.is_zero()) + } + + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } + +/* + // not tested, gives Immediate18TooLarge error + pub fn square(self) -> Fp2 { + // Complex squaring: + // + // v0 = c0 * c1 + // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 + // c1' = 2 * v0 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula: + // + // c0' = (c0 + c1) * (c0 - c1) + // c1' = 2 * c0 * c1 + + let a = self.c0 + self.c1; + let b = self.c0 - self.c1; + let c = self.c0 + self.c0; + + Fp2 { + c0: a * b, + c1: c * self.c1, + } + } + */ + + pub fn mul(self, rhs: Fp2) -> Fp2 { + Fp2 { + c0: ~Fp::sum_of_products_2([self.c0, self.c1.neg()], [rhs.c0, rhs.c1]), + c1: ~Fp::sum_of_products_2([self.c0, self.c1], [rhs.c1, rhs.c0]), + } + } + + pub fn add(self, rhs: Fp2) -> Fp2 { + Fp2 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + } + } + + pub fn sub(self, rhs: Fp2) -> Fp2 { + Fp2 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + } + } + + pub fn neg(self) -> Fp2 { + Fp2 { + c0: (self.c0).neg(), + c1: (self.c1).neg(), + } + } + + // Is not tested directly, but will be indirectly + pub fn mul_by_nonresidue(self) -> Fp2 { + // Multiply a + bu by u + 1, getting + // au + a + bu^2 + bu + // and because u^2 = -1, we get + // (a - b) + (a + b)u + + Fp2 { + c0: self.c0 - self.c1, + c1: self.c0 + self.c1, + } + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + pub fn lexicographically_largest(self) -> Choice { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. + + self.c1.lexicographically_largest() + .binary_or(self.c1.is_zero().binary_and(self.c0.lexicographically_largest())) + } + + pub fn conjugate(self) -> Fp2 { + Fp2{ + c0: self.c0, + c1: (self.c1).neg(), + } + } + + /// Raises this element to p. + pub fn frobenius_map(self) -> Fp2 { + //needs to be verified + // This is always just a conjugation. If you're curious why, here's + // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ + // self.conjugate() //showing error + Fp2{ + c0: self.c0, + c1: (self.c1).neg(), + } + } + +} + +impl Eq for Fp2 { + fn eq(self, other: Self) -> bool { + self.eq(other) + } +} + +impl Add for Fp2 { + fn add(self, other: Self) -> Self { + self.add(other) + } +} + +impl Subtract for Fp2 { + fn subtract(self, other: Self) -> Self { + self.sub(other) + } +} + +impl Multiply for Fp2 { + fn multiply(self, other: Self) -> Self { + self.mul(other) + } +} \ No newline at end of file diff --git a/bls12_381/src/fp6.sw b/bls12_381/src/fp6.sw new file mode 100644 index 0000000..21333ab --- /dev/null +++ b/bls12_381/src/fp6.sw @@ -0,0 +1,312 @@ +library fp6; + +dep fp2; +dep fp; +dep choice; + +use fp::{Fp, from_raw_unchecked}; +use fp2::Fp2; +use choice::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable}; +use core::ops::{Eq, Add, Subtract, Multiply}; + +pub struct Fp6 { + c0: Fp2, + c1: Fp2, + c2: Fp2, +} + +impl ConstantTimeEq for Fp6 { + fn ct_eq(self, other: Self) -> Choice { + self.c0.ct_eq(other.c0) & self.c1.ct_eq(other.c1) & self.c2.ct_eq(other.c2) + } +} + +impl ConditionallySelectable for Fp6 { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + Fp6 { + c0: ~Fp2::conditional_select(a.c0, b.c0, choice), + c1: ~Fp2::conditional_select(a.c1, b.c1, choice), + c2: ~Fp2::conditional_select(a.c2, b.c2, choice), + } + } +} + +impl Fp6 { + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } + + fn from(f: Fp) -> Fp6 {//is it possibly to have multiple functions with same name and different arguments? + Fp6 { + c0: ~Fp2::from(f), + c1: ~Fp2::zero(), + c2: ~Fp2::zero(), + } + } + + fn from(f: Fp2) -> Fp6 { + Fp6 { + c0: f, + c1: ~Fp2::zero(), + c2: ~Fp2::zero(), + } + } + + pub fn zero() -> Self { + Fp6 { + c0: ~Fp2::zero(), + c1: ~Fp2::zero(), + c2: ~Fp2::zero(), + } + } + + pub fn one() -> Self { + Fp6 { + c0: ~Fp2::one(), + c1: ~Fp2::zero(), + c2: ~Fp2::zero(), + } + } + + pub fn is_zero(self) -> Choice { + self.c0.is_zero().binary_and(self.c1.is_zero()).binary_and(self.c2.is_zero()) + } + /* + +//TODO test (but zkcrypto doesnt have a dedicated test, so will be tested implicitly) + pub fn mul_by_1(self, c1: Fp2) -> Fp6 { + let b_b = self.c1 * c1; + + let t1 = (self.c1 + self.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue(); + + let t2 = (self.c0 + self.c1) * c1 - b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: b_b, + } + } + //TODO: Testing. Has no dedicated tests in zkcrypto + pub fn mul_by_01(self, c0: Fp2, c1: Fp2) -> Fp6 { + let a_a = self.c0 * c0; + let b_b = self.c1 * c1; + + let t1 = (self.c1 + self.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue() + a_a; + + let t2 = (c0 + c1) * (self.c0 +self.c1) - a_a -b_b; + + let t3 = (self.c0 + self.c2) * c0 - a_a + b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: t3, + } + } + */ + + pub fn mul_by_nonresidue(self) -> Self { + // Given a + bv + cv^2, this produces + // av + bv^2 + cv^3 + // but because v^3 = u + 1, we have + // c(u + 1) + av + v^2 + + Fp6 { + c0: self.c2.mul_by_nonresidue(), + c1: self.c0, + c2: self.c1, + } + } + + //TODO: Testing. Has no dedicated tests in zkcrypto + pub fn add(self, rhs: Fp6) -> Fp6 { + Fp6 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + c2: self.c2 + rhs.c2, + } + } + //TODO: Testing. Has no dedicated tests in zkcrypto + pub fn sub(self, rhs: Fp6) -> Fp6 { + Fp6 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + c2: self.c2 - rhs.c2, + } + } + //TODO: Testing. Has no dedicated tests in zkcrypto + pub fn neg(self) -> Fp6 { + Fp6 { + c0: self.c0.neg(), + c1: self.c1.neg(), + c2: self.c2.neg(), + } + } + + fn conditional_select(a: Fp6, b: Fp6, choice: Choice) -> Fp6 { + Fp6 { + c0: ~Fp2::conditional_select(a.c0, b.c0, choice), + c1: ~Fp2::conditional_select(a.c1, b.c1, choice), + c2: ~Fp2::conditional_select(a.c2, b.c2, choice), + } + } + +/* + // not tested, gives Immediate18TooLarge error + fn mul_interleaved(self, b: Self) -> Self { + // The intuition for this algorithm is that we can look at F_p^6 as a direct + // extension of F_p^2, and express the overall operations down to the base field + // F_p instead of only over F_p^2. This enables us to interleave multiplications + // and reductions, ensuring that we don't require double-width intermediate + // representations (with around twice as many limbs as F_p elements). + + // We want to express the multiplication c = a x b, where a = (a_0, a_1, a_2) is + // an element of F_p^6, and a_i = (a_i,0, a_i,1) is an element of F_p^2. The fully + // expanded multiplication is given by (2022-376 ยง5): + // + // c_0,0 = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1 + // - a_1,0 b_2,1 - a_1,1 b_2,0 - a_2,0 b_1,1 - a_2,1 b_1,0. + // = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 (b_2,0 - b_2,1) - a_1,1 (b_2,0 + b_2,1) + // + a_2,0 (b_1,0 - b_1,1) - a_2,1 (b_1,0 + b_1,1). + // + // c_0,1 = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0 b_2,1 + a_1,1 b_2,0 + a_2,0 b_1,1 + a_2,1 b_1,0 + // + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1. + // = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0(b_2,0 + b_2,1) + a_1,1(b_2,0 - b_2,1) + // + a_2,0(b_1,0 + b_1,1) + a_2,1(b_1,0 - b_1,1). + // + // c_1,0 = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0 b_2,0 - a_2,1 b_2,1 + // - a_2,0 b_2,1 - a_2,1 b_2,0. + // = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0(b_2,0 - b_2,1) + // - a_2,1(b_2,0 + b_2,1). + // + // c_1,1 = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0 b_2,1 + a_2,1 b_2,0 + // + a_2,0 b_2,0 - a_2,1 b_2,1 + // = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0(b_2,0 + b_2,1) + // + a_2,1(b_2,0 - b_2,1). + // + // c_2,0 = a_0,0 b_2,0 - a_0,1 b_2,1 + a_1,0 b_1,0 - a_1,1 b_1,1 + a_2,0 b_0,0 - a_2,1 b_0,1. + // c_2,1 = a_0,0 b_2,1 + a_0,1 b_2,0 + a_1,0 b_1,1 + a_1,1 b_1,0 + a_2,0 b_0,1 + a_2,1 b_0,0. + // + // Each of these is a "sum of products", which we can compute efficiently. + + let a = self; + let b10_p_b11 = b.c1.c0 + b.c1.c1; + let b10_m_b11 = b.c1.c0 - b.c1.c1; + let b20_p_b21 = b.c2.c0 + b.c2.c1; + let b20_m_b21 = b.c2.c0 - b.c2.c1; + + Fp6 { + c0: Fp2 { + c0: ~Fp::sum_of_products_6( + [a.c0.c0, a.c0.c1.neg(), a.c1.c0, a.c1.c1.neg(), a.c2.c0, a.c2.c1.neg()], + [b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11], + ), + c1: ~Fp::sum_of_products_6( + [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1], + [b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11], + ), + }, + c1: Fp2 { + c0: ~Fp::sum_of_products_6( + [a.c0.c0, a.c0.c1.neg(), a.c1.c0, a.c1.c1.neg(), a.c2.c0, a.c2.c1.neg()], + [b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21], + ), + c1: ~Fp::sum_of_products_6( + [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1], + [b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21], + ), + }, + c2: Fp2 { + c0: ~Fp::sum_of_products_6( + [a.c0.c0, a.c0.c1.neg(), a.c1.c0, a.c1.c1.neg(), a.c2.c0, a.c2.c1.neg()], + [b.c2.c0, b.c2.c1, b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1], + ), + c1: ~Fp::sum_of_products_6( + [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1], + [b.c2.c1, b.c2.c0, b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0], + ), + }, + } + } + */ +/* + pub fn square(self) -> Fp6 { + let s0 = self.c0.square(); + let ab = self.c0 * self.c1; + let s1 = ab + ab; + let s2 = (self.c0 - self.c1 + self.c2).square(); + let bc = self.c1 * self.c2; + let s3 = bc + bc; + let s4 = self.c2.square(); + + Fp6 { + c0: s3.mul_by_nonresidue() + s0, + c1: s4.mul_by_nonresidue() + s1, + c2: s1 + s2 + s3 - s0 - s4, + } + }*/ + +/* + pub fn frobenius_map(self) -> Fp6 { + let c0 = (self.c0).frobenius_map(); + let c1 = (self.c1).frobenius_map(); + let c2 = (self.c2).frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 3) + + let c1 = c1 * Fp2 { + c0: ~Fp::zero(), + c1: from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + }; + let c2 = c2 * Fp2 { + c0: from_raw_unchecked([ + 0x890d_c9e4_8675_45c3, + 0x2af3_2253_3285_a5d5, + 0x5088_0866_309b_7e2c, + 0xa20d_1b8c_7e88_1024, + 0x14e4_f04f_e2db_9068, + 0x14e5_6d3f_1564_853a, + ]), + c1: ~Fp::zero(), + }; + + Fp6 { + c0, c1, c2 + } + } + */ +} + +impl Eq for Fp6 { + fn eq(self, other: Self) -> bool { + self.eq(other) + } +} + +impl Add for Fp6 { + fn add(self, other: Self) -> Self { + self.add(other) + } +} + +impl Subtract for Fp6 { + fn subtract(self, other: Self) -> Self { + self.sub(other) + } +} + +// impl Multiply for Fp6 { +// fn multiply(self, other: Self) -> Self { +// self.mul_interleaved(other) +// } +// } \ No newline at end of file diff --git a/bls12_381/src/g1.sw b/bls12_381/src/g1.sw new file mode 100644 index 0000000..63e8118 --- /dev/null +++ b/bls12_381/src/g1.sw @@ -0,0 +1,420 @@ +library g1; + +dep fp; +dep choice; + +use fp::{Fp, from_raw_unchecked}; +use choice::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq}; +use core::ops::{Eq, Add, Subtract}; + + +// Comment from zkcrypto +/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +pub struct G1Affine { + x: Fp, + y: Fp, + infinity: Choice, +} + +pub const B: Fp = from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, +]); + +fn mul_by_3b(a: Fp) -> Fp { + let a = a + a; // 2 + let a = a + a; // 4 + a + a + a // 12 +} + +impl ConstantTimeEq for G1Affine { + fn ct_eq(self, other: Self) -> Choice { + // Comment from zkcrypto + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (self.infinity & other.infinity) + .binary_or( + (self.infinity.not()) + .binary_and(other.infinity.not()) + .binary_and(self.x.ct_eq(other.x)) + .binary_and(self.y.ct_eq(other.y)) + ) + } +} + +pub trait From_Proj { + fn from(p: G1Projective) -> Self; +} + +fn unwrap_or(input: CtOption, default: Fp) -> Fp { + match input.is_some() { + true => input.unwrap(), + false => default, + } +} + +//TODO +// - needs fp invert, which is not working yet +// - will use already created fn `unwrap_or` since adding unwrap_or to trait CtOption can't work yet +// impl From_Proj for G1Affine { +// fn from(p: G1Projective) -> Self { +// .. +// } +// } + +impl ConditionallySelectable for G1Affine { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + G1Affine { + x: ~Fp::conditional_select(a.x, b.x, choice), + y: ~Fp::conditional_select(a.y, b.y, choice), + infinity: ~Choice::conditional_select(a.infinity, b.infinity, choice), + } + } +} + +impl Eq for G1Affine { + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } +} + +impl G1Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Affine { + G1Affine { + x: ~Fp::zero(), + y: ~Fp::one(), + infinity: ~Choice::from(1u8), + } + } + + pub fn is_identity(self) -> Choice { + self.infinity + } + + // TODO TEST WHEN POSSIBLE: Uses mul_by_x on G1Projective which uses double, which can't compile + // pub fn is_torsion_free(self) -> Choice { + // Comment from zkcrypto + // // Algorithm from Section 6 of https://eprint.iacr.org/2021/1130 + // // Updated proof of correctness in https://eprint.iacr.org/2022/352 + // // + // // Check that endomorphism_p(P) == -[x^2] P + + // let minus_x_squared_times_p = from(self).mul_by_x().mul_by_x().neg(); + // let endomorphism_p = endomorphism(self); + // minus_x_squared_times_p.ct_eq(from(endomorphism_p)) + // } + + //Errors to Immediate18TooLarge + // pub fn is_on_curve(self) -> Choice { + // // y^2 - x^3 ?= 4 + // (self.y.square() - (self.x.square() * self.x)).ct_eq(B) | self.infinity + // } + + pub fn generator() -> G1Affine { + G1Affine { + x: from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + infinity: ~Choice::from(0u8), + } + } + + fn neg(self) -> G1Affine {//will be tested with subtraction (TODO) + G1Affine { + x: self.x, + y: ~Fp::conditional_select(self.y.neg(), ~Fp::one(), self.infinity), + infinity: self.infinity, + } + } +} + +// Comment from zkcrypto +/// A nontrivial third root of unity in Fp +pub const BETA: Fp = from_raw_unchecked([ + 0x30f1_361b_798a_64e8, + 0xf3b8_ddab_7ece_5a2a, + 0x16a8_ca3a_c615_77f7, + 0xc26a_2ff8_74fd_029b, + 0x3636_b766_6070_1c6e, + 0x051b_a4ab_241b_6160, +]); + +fn endomorphism(p: G1Affine) -> G1Affine { + // Comment from zkcrypto + // Endomorphism of the points on the curve. + // endomorphism_p(x,y) = (BETA * x, y) + // where BETA is a non-trivial cubic root of unity in Fq. + let mut res = p; + res.x *= BETA; + res +} + +pub struct G1Projective { + x: Fp, + y: Fp, + z: Fp, +} + + +impl G1Projective { + // Comment from zkcrypto + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Projective { + G1Projective { + x: ~Fp::zero(), + y: ~Fp::one(), + z: ~Fp::zero(), + } + } + + pub fn is_identity(self) -> Choice { + self.z.is_zero() + } + + pub fn neg(self) -> G1Projective { //will be tested with subtraction (TODO) + G1Projective { + x: self.x, + y: self.y.neg(), + z: self.z, + } + } + + //Errors to Immediate18TooLarge + // pub fn is_on_curve(self) -> Choice { + // // Y^2 Z = X^3 + b Z^3 + + // (self.y.square() * self.z).ct_eq(self.x.square() * self.x + self.z.square() * self.z * B) + // | self.z.is_zero() + // } + + pub fn generator() -> G1Projective { + G1Projective { + x: from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + z: ~Fp::one(), + } + } +} + +impl ConditionallySelectable for G1Projective { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + G1Projective { + x: ~Fp::conditional_select(a.x, b.x, choice), + y: ~Fp::conditional_select(a.y, b.y, choice), + z: ~Fp::conditional_select(a.z, b.z, choice), + } + } +} + +impl G1Projective { + + // Not able to test this yet, doesn't terminate + /// Computes the doubling of this point. + pub fn double(self) -> G1Projective { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.y.square(); + let z3 = t0 + t0; + let z3 = z3 + z3; + let z3 = z3 + z3; + let t1 = self.y * self.z; + let t2 = self.z.square(); + let t2 = mul_by_3b(t2); + let x3 = t2 * z3; + let y3 = t0 + t2; + let z3 = t1 * z3; + let t1 = t2 + t2; + let t2 = t1 + t2; + let t0 = t0 - t2; + let y3 = t0 * y3; + let y3 = x3 + y3; + let t1 = self.x * self.y; + let x3 = t0 * t1; + let x3 = x3 + x3; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + ~G1Projective::conditional_select(tmp, ~G1Projective::identity(), self.is_identity()) + } + + pub fn add(self, rhs: G1Projective) -> G1Projective { + // Comment from zkcrypto + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t2 = self.z * rhs.z; + let t3 = self.x + self.y; + let t4 = rhs.x + rhs.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = self.y + self.z; + let x3 = rhs.y + rhs.z; + let t4 = t4 * x3; + let x3 = t1 + t2; + let t4 = t4 - x3; + let x3 = self.x + self.z; + let y3 = rhs.x + rhs.z; + let x3 = x3 * y3; + let y3 = t0 + t2; + let y3 = x3 - y3; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = mul_by_3b(t2); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = mul_by_3b(y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + G1Projective { + x: x3, + y: y3, + z: z3, + } + } + +//TODO TEST + pub fn add_mixed(self, rhs: G1Affine) -> G1Projective { + // Comment from zkcrypto + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t3 = rhs.x + rhs.y; + let t4 = self.x + self.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = rhs.y * self.z; + let t4 = t4 + self.y; + let y3 = rhs.x * self.z; + let y3 = y3 + self.x; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = mul_by_3b(self.z); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = mul_by_3b(y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + ~G1Projective::conditional_select(tmp, self, rhs.is_identity()) + } +} + +pub trait From_Aff { + fn from(p: G1Affine) -> Self; +} + +impl From_Aff for G1Projective { + fn from(p: G1Affine) -> Self { + G1Projective { + x: p.x, + y: p.y, + z: ~Fp::conditional_select(~Fp::one(), ~Fp::zero(), p.infinity), + } + } +} + +impl ConstantTimeEq for G1Projective { + fn ct_eq(self, other: Self) -> Choice { + // Comments from zkcrypto + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? + + let x1 = self.x * other.z; + let x2 = other.x * self.z; + + let y1 = self.y * other.z; + let y2 = other.y * self.z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + self_is_zero.binary_and(other_is_zero) // Both point at infinity + .binary_or(((~Choice::not(self_is_zero)).binary_and(~Choice::not(other_is_zero)).binary_and(x1.ct_eq(x2).binary_and(y1.ct_eq(y2))))) + // Neither point at infinity, coordinates are the same + } +} + +impl Eq for G1Projective { + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } +} + +impl Add for G1Projective { + fn add(self, other: Self) -> Self { + self.add(other) + } +} + +impl Subtract for G1Projective { + fn subtract(self, other: Self) -> Self { + self + (other.neg()) + } +} \ No newline at end of file diff --git a/bls12_381/src/lib.sw b/bls12_381/src/lib.sw new file mode 100644 index 0000000..35ac567 --- /dev/null +++ b/bls12_381/src/lib.sw @@ -0,0 +1,7 @@ +library bls12_381; + +dep fp; +dep fp2; +dep fp6; +dep scalar; +dep choice; \ No newline at end of file diff --git a/bls12_381/src/main.sw b/bls12_381/src/main.sw new file mode 100644 index 0000000..0a1ebc1 --- /dev/null +++ b/bls12_381/src/main.sw @@ -0,0 +1,258 @@ +script; + +// dep fp; +// dep fp2; +dep g1; //compiles but takes a really long time +// dep choice; +// dep util; +// dep scalar; + +// use ::fp::{Fp, from_raw_unchecked}; +// use ::fp2::Fp2; +use ::g1::G1Projective; +// use ::choice::*; +use std::{assert::assert}; +// use std::logging::log; +// use ::g1::G1Affine; +// use ::util::*; +// use ::scalar::*; + +//this is a temporary file for test purposes. +// Logging in a library is easier when testing with a script. When testing through a contract that's not possible + +fn main () { + // assert(test_square_fp()); + // assert(test_mul_fp2()); + // test_double_identity(); + // assert(test_wrap_neg()); + // assert(test_ct_eq()); + // assert(test_is_zero()); + // assert(test_is_eq()); + // assert(test_conditional_select()); + // assert(test_g1_equality()); + // assert(test_opposite_choice_value()); + // assert(test_add_g1()); + // assert(test_scalar_equality()); + // assert(test_scalar_neg()); + + assert(test_double_identity()); +} + +// pub fn test_scalar_neg() -> bool { +// let largest = Scalar { ls: [ +// 0xffff_ffff_0000_0000, +// 0x53bd_a402_fffe_5bfe, +// 0x3339_d808_09a1_d805, +// 0x73ed_a753_299d_7d48, +// ]}; +// let neg_largest = largest.neg(); +// assert(neg_largest == Scalar { ls: [1, 0, 0, 0]}); + +// let zero = Scalar { ls: [0,0,0,0]}; +// let neg_zero = zero.neg(); +// assert(neg_zero == Scalar { ls: [0,0,0,0]} ); + +// let neg_one = (Scalar { ls: [1,0,0,0]}).neg(); +// assert(neg_one == largest); + +// true +// } + +// pub fn test_scalar_equality() -> bool { +// assert(R2 == R2); +// true +// } + +// pub fn test_opposite_choice_value() -> bool { +// !opposite_choice_value(1u8) && opposite_choice_value(0u8) +// }x + +/*G1 compilation takes forever atm +fn test_add_g1() -> bool { + let a = G1Projective { + x: from_raw_unchecked([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + z: ~Fp::one() + }; + + let first = a.add(a); + // let second = ~G1Projective::identity(); + // res_equals(first.x, second.x); + // res_equals(first.y, second.y); + // res_equals(first.z, second.z); + true +} + +pub fn test_g1_equality() -> bool { + let p = G1Affine { + x: from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + infinity: ~Choice::from(0u8), + }; + let a = ~G1Affine::identity(); + a.eq(a) && p.eq(p) && !a.eq(p) +} +*/ + +// pub fn test_conditional_select() -> bool { +// let first_check = ~u64::conditional_select(10, 100, ~Choice::from(1)) == 10; +// let second_check = ~u64::conditional_select(10, 100, ~Choice::from(0)) == 100; +// first_check && second_check +// } + +// pub fn test_is_zero() -> bool { +// ~Fp::is_zero(~Fp::zero()).unwrap_as_bool() +// } + +// pub fn test_is_eq() -> bool { +// let a = Fp{ ls: [ +// 0xf597_483e_27b4_e0f7, +// 0x610f_badf_811d_ae5f, +// 0x8432_af91_7714_327a, +// 0x6a9a_9603_cf88_f09e, +// 0xf05a_7bf8_bad0_eb01, +// 0x0954_9131_c003_ffae, +// ]}; +// (a == a) && (~Fp::zero() == ~Fp::zero()) +// } + +// pub fn test_wrap_neg() -> bool { +// let x: u64 = 100; +// let res = wrapping_neg(x); +// res == 18446744073709551516 +// } + +// pub fn test_ct_eq() -> bool { +// ~u64::ct_eq(100, (100)).unwrap_as_bool() +// } + +// This doesn't terminate (or does it maybe give the Immediate18TooLarge after forever?) +fn test_double_identity() -> bool { + let p_id = ~G1Projective::identity(); + let doubled = p_id.double(); + true +} + + +// pub fn res_equals(a: Fp, b: Fp) -> bool { +// assert(a.ls[0] == b.ls[0]); +// assert(a.ls[1] == b.ls[1]); +// assert(a.ls[2] == b.ls[2]); +// assert(a.ls[3] == b.ls[3]); +// assert(a.ls[4] == b.ls[4]); +// assert(a.ls[5] == b.ls[5]); +// true +// } + +// fn test_square_fp() -> bool { +// let a: Fp = Fp { +// ls: [0xd215_d276_8e83_191b, +// 0x5085_d80f_8fb2_8261, +// 0xce9a_032d_df39_3a56, +// 0x3e9c_4fff_2ca0_c4bb, +// 0x6436_b6f7_f4d9_5dfb, +// 0x1060_6628_ad4a_4d90] +// }; + +// let expected_res: Fp = Fp { +// ls: [0x33d9_c42a_3cb3_e235, +// 0xdad1_1a09_4c4c_d455, +// 0xa2f1_44bd_729a_aeba, +// 0xd415_0932_be9f_feac, +// 0xe27b_c7c4_7d44_ee50, +// 0x14b6_a78d_3ec7_a560] +// }; + +// let res = a.square(); +// res_equals(res, expected_res); +// true +// } + +// fn test_mul_fp2() -> bool { +// let a = Fp2 { +// c0: Fp{ ls: [ +// 0xc9a2_1831_63ee_70d4, +// 0xbc37_70a7_196b_5c91, +// 0xa247_f8c1_304c_5f44, +// 0xb01f_c2a3_726c_80b5, +// 0xe1d2_93e5_bbd9_19c9, +// 0x04b7_8e80_020e_f2ca, +// ]}, +// c1: Fp{ ls: [ +// 0x952e_a446_0462_618f, +// 0x238d_5edd_f025_c62f, +// 0xf6c9_4b01_2ea9_2e72, +// 0x03ce_24ea_c1c9_3808, +// 0x0559_50f9_45da_483c, +// 0x010a_768d_0df4_eabc, +// ]}, +// }; +// let b = Fp2 { +// c0: Fp{ ls: [ +// 0xa1e0_9175_a4d2_c1fe, +// 0x8b33_acfc_204e_ff12, +// 0xe244_15a1_1b45_6e42, +// 0x61d9_96b1_b6ee_1936, +// 0x1164_dbe8_667c_853c, +// 0x0788_557a_cc7d_9c79, +// ]}, +// c1: Fp{ ls: [ +// 0xda6a_87cc_6f48_fa36, +// 0x0fc7_b488_277c_1903, +// 0x9445_ac4a_dc44_8187, +// 0x0261_6d5b_c909_9209, +// 0xdbed_4677_2db5_8d48, +// 0x11b9_4d50_76c7_b7b1, +// ]}, +// }; +// let c = Fp2 { +// c0: Fp{ ls: [ +// 0xf597_483e_27b4_e0f7, +// 0x610f_badf_811d_ae5f, +// 0x8432_af91_7714_327a, +// 0x6a9a_9603_cf88_f09e, +// 0xf05a_7bf8_bad0_eb01, +// 0x0954_9131_c003_ffae, +// ]}, +// c1: Fp{ ls: [ +// 0x963b_02d0_f93d_37cd, +// 0xc95c_e1cd_b30a_73d4, +// 0x3087_25fa_3126_f9b8, +// 0x56da_3c16_7fab_0d50, +// 0x6b50_86b5_f4b6_d6af, +// 0x09c3_9f06_2f18_e9f2, +// ]}, +// }; +// let res = a.mul(b); +// res_equals(res.c0, c.c0); +// res_equals(res.c1, c.c1); +// true +// } \ No newline at end of file diff --git a/bls12_381/src/scalar.sw b/bls12_381/src/scalar.sw new file mode 100644 index 0000000..0fc3d93 --- /dev/null +++ b/bls12_381/src/scalar.sw @@ -0,0 +1,415 @@ +library scalar; + +dep choice; +dep util; + +//This import is needed because of importing ConstantTimeEq for u64 (since it's a trait for a primitive type) +use choice::*; +use util::*; + +use core::ops::{Eq, Add, Subtract, Multiply}; + +pub struct Scalar { ls: [u64; 4] } + +/// Constant representing the modulus +/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +pub const MODULUS_SCALAR: Scalar = Scalar{ ls: [ + 0xffff_ffff_0000_0001, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, +]}; + +/// The modulus as u32 limbs. +const MODULUS_LIMBS_32: [u32; 8] = [ + 0x0000_0001, + 0xffff_ffff, + 0xfffe_5bfe, + 0x53bd_a402, + 0x09a1_d805, + 0x3339_d808, + 0x299d_7d48, + 0x73ed_a753, +]; + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 255; + +const GENERATOR: Scalar = Scalar{ ls: [ + 0x0000_000e_ffff_fff1, + 0x17e3_63d3_0018_9c0f, + 0xff9c_5787_6f84_57b0, + 0x3513_3220_8fc5_a8c4, +]}; + +/// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xffff_fffe_ffff_ffff; + +/// R = 2^256 mod q +const R: Scalar = Scalar{ ls: [ + 0x0000_0001_ffff_fffe, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f, +]}; + +/// R^2 = 2^512 mod q +pub const R2: Scalar = Scalar{ ls: [ + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11, +]}; + +/// R^3 = 2^768 mod q +const R3: Scalar = Scalar{ ls: [ + 0xc62c_1807_439b_73af, + 0x1b3e_0d18_8cf0_6990, + 0x73d1_3c71_c7b5_f418, + 0x6e2a_5bb9_c8db_33e9, +]}; + +// 2^S * t = MODULUS - 1 with t odd +const S: u32 = 32; + +const ROOT_OF_UNITY: Scalar = Scalar{ ls: [ + 0xb9b5_8d8c_5f0e_466a, + 0x5b1b_4c80_1819_d7ec, + 0x0af5_3ae3_52a3_1e64, + 0x5bf3_adda_19e9_b27b, +]}; + +impl ConditionallySelectable for Scalar { + fn conditional_select(a: Self, b: Self, choice: Choice) -> Self { + Scalar{ ls: [ + ~u64::conditional_select(a.ls[0], b.ls[0], choice), + ~u64::conditional_select(a.ls[1], b.ls[1], choice), + ~u64::conditional_select(a.ls[2], b.ls[2], choice), + ~u64::conditional_select(a.ls[3], b.ls[3], choice), + ]} + } +} + +impl ConstantTimeEq for Scalar { + fn ct_eq(self, other: Self) -> Choice { + ~u64::ct_eq(self.ls[0], other.ls[0]) + .binary_and(~u64::ct_eq(self.ls[1], other.ls[1])) + .binary_and(~u64::ct_eq(self.ls[2], other.ls[2])) + .binary_and(~u64::ct_eq(self.ls[3], other.ls[3])) + } +} + +impl Scalar { + + pub fn zero() -> Scalar { + Scalar{ ls: [0, 0, 0, 0]} + } + + pub fn one() -> Scalar { + R + } + + pub fn sub(self, rhs: Self) -> Self { + let (d0, borrow) = sbb(self.ls[0], rhs.ls[0], 0); + let (d1, borrow) = sbb(self.ls[1], rhs.ls[1], borrow); + let (d2, borrow) = sbb(self.ls[2], rhs.ls[2], borrow); + let (d3, borrow) = sbb(self.ls[3], rhs.ls[3], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000.. .ls00. Thus, we use it as a mask to conditionally add the modulus. + let (d0, carry) = adc(d0, MODULUS_SCALAR.ls[0] & borrow, 0); + let (d1, carry) = adc(d1, MODULUS_SCALAR.ls[1] & borrow, carry); + let (d2, carry) = adc(d2, MODULUS_SCALAR.ls[2] & borrow, carry); + let (d3, _) = adc(d3, MODULUS_SCALAR.ls[3] & borrow, carry); + + Scalar{ ls: [d0, d1, d2, d3]} + } + +//E: This was tested through script and works. Didn't add test to contract because contract testing is slow and this will be tested implicitly as well + pub fn neg(self) -> Self { + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + let (d0, borrow) = sbb(MODULUS_SCALAR.ls[0], self.ls[0], 0); + let (d1, borrow) = sbb(MODULUS_SCALAR.ls[1], self.ls[1], borrow); + let (d2, borrow) = sbb(MODULUS_SCALAR.ls[2], self.ls[2], borrow); + let (d3, _) = sbb(MODULUS_SCALAR.ls[3], self.ls[3], borrow); + + // The mask should be 0 when a==p and 2^65-1 otherwise + // limbs = 0 when self = 0 + let limbs = self.ls[0] | self.ls[1] | self.ls[2] | self.ls[3]; + // p mod p = 0, so this checks whether self is p + let scalar_is_0_mod_p = is_zero_u64(limbs); + // mask = a_is_p - 1. This will give either 1-1 (=0) or 0-1 (wrap around to 2^64-1) + let mask = subtract_1_wrap(scalar_is_0_mod_p); + + Scalar{ ls: [d0 & mask, d1 & mask, d2 & mask, d3 & mask]} + } +} + +impl Scalar { + + /// Adds `rhs` to `self`, returning the result. + pub fn add(self, rhs: Self) -> Self { + let (d0, carry) = adc(self.ls[0], rhs.ls[0], 0); + let (d1, carry) = adc(self.ls[1], rhs.ls[1], carry); + let (d2, carry) = adc(self.ls[2], rhs.ls[2], carry); + let (d3, _) = adc(self.ls[3], rhs.ls[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (Scalar{ls:[d0, d1, d2, d3]}).sub(MODULUS_SCALAR) + } + + pub fn montgomery_reduce( + r0: u64, + r1: u64, + r2: u64, + r3: u64, + r4: u64, + r5: u64, + r6: u64, + r7: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = wrapping_mul(r0, INV); + let (_, carry) = mac(r0, k, MODULUS_SCALAR.ls[0], 0); + let (r1, carry) = mac(r1, k, MODULUS_SCALAR.ls[1], carry); + let (r2, carry) = mac(r2, k, MODULUS_SCALAR.ls[2], carry); + let (r3, carry) = mac(r3, k, MODULUS_SCALAR.ls[3], carry); + let (r4, carry2) = adc(r4, 0, carry); + + let k = wrapping_mul(r1, INV); + let (_, carry) = mac(r1, k, MODULUS_SCALAR.ls[0], 0); + let (r2, carry) = mac(r2, k, MODULUS_SCALAR.ls[1], carry); + let (r3, carry) = mac(r3, k, MODULUS_SCALAR.ls[2], carry); + let (r4, carry) = mac(r4, k, MODULUS_SCALAR.ls[3], carry); + let (r5, carry2) = adc(r5, carry2, carry); + + let k = wrapping_mul(r2, INV); + let (_, carry) = mac(r2, k, MODULUS_SCALAR.ls[0], 0); + let (r3, carry) = mac(r3, k, MODULUS_SCALAR.ls[1], carry); + let (r4, carry) = mac(r4, k, MODULUS_SCALAR.ls[2], carry); + let (r5, carry) = mac(r5, k, MODULUS_SCALAR.ls[3], carry); + let (r6, carry2) = adc(r6, carry2, carry); + + let k = wrapping_mul(r3, INV); + let (_, carry) = mac(r3, k, MODULUS_SCALAR.ls[0], 0); + let (r4, carry) = mac(r4, k, MODULUS_SCALAR.ls[1], carry); + let (r5, carry) = mac(r5, k, MODULUS_SCALAR.ls[2], carry); + let (r6, carry) = mac(r6, k, MODULUS_SCALAR.ls[3], carry); + let (r7, _) = adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + (Scalar{ ls:[r4, r5, r6, r7]}).sub(MODULUS_SCALAR) + } +} + +impl Scalar { + pub fn mul(self, rhs: Self) -> Self { + // Schoolbook multiplication + + let (r0, carry) = mac(0, self.ls[0], rhs.ls[0], 0); + let (r1, carry) = mac(0, self.ls[0], rhs.ls[1], carry); + let (r2, carry) = mac(0, self.ls[0], rhs.ls[2], carry); + let (r3, r4) = mac(0, self.ls[0], rhs.ls[3], carry); + + let (r1, carry) = mac(r1, self.ls[1], rhs.ls[0], 0); + let (r2, carry) = mac(r2, self.ls[1], rhs.ls[1], carry); + let (r3, carry) = mac(r3, self.ls[1], rhs.ls[2], carry); + let (r4, r5) = mac(r4, self.ls[1], rhs.ls[3], carry); + + let (r2, carry) = mac(r2, self.ls[2], rhs.ls[0], 0); + let (r3, carry) = mac(r3, self.ls[2], rhs.ls[1], carry); + let (r4, carry) = mac(r4, self.ls[2], rhs.ls[2], carry); + let (r5, r6) = mac(r5, self.ls[2], rhs.ls[3], carry); + + let (r3, carry) = mac(r3, self.ls[3], rhs.ls[0], 0); + let (r4, carry) = mac(r4, self.ls[3], rhs.ls[1], carry); + let (r5, carry) = mac(r5, self.ls[3], rhs.ls[2], carry); + let (r6, r7) = mac(r6, self.ls[3], rhs.ls[3], carry); + + ~Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + pub fn square(self) -> Scalar { + let (r1, carry) = mac(0, self.ls[0], self.ls[1], 0); + let (r2, carry) = mac(0, self.ls[0], self.ls[2], carry); + let (r3, r4) = mac(0, self.ls[0], self.ls[3], carry); + + let (r3, carry) = mac(r3, self.ls[1], self.ls[2], 0); + let (r4, r5) = mac(r4, self.ls[1], self.ls[3], carry); + + let (r5, r6) = mac(r5, self.ls[2], self.ls[3], 0); + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let (r0, carry) = mac(0, self.ls[0], self.ls[0], 0); + let (r1, carry) = adc(0, r1, carry); + let (r2, carry) = mac(r2, self.ls[1], self.ls[1], carry); + let (r3, carry) = adc(0, r3, carry); + let (r4, carry) = mac(r4, self.ls[2], self.ls[2], carry); + let (r5, carry) = adc(0, r5, carry); + let (r6, carry) = mac(r6, self.ls[3], self.ls[3], carry); + let (r7, _) = adc(0, r7, carry); + + ~Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + pub fn double(self) -> Scalar { + // zkcrypto comment: TODO: This can be achieved more efficiently with a bitshift. + self.add(self) + } +} + +impl Add for Scalar { + fn add(self, other: Self) -> Self { + self.add(other) + } +} + +impl Multiply for Scalar { + fn multiply(self, other: Self) -> Self { + self.mul(other) + } +} + +impl Eq for Scalar { + fn eq(self, other: Self) -> bool { + self.ct_eq(other).unwrap_as_bool() + } +} + +impl Scalar { + + fn from(val: u64) -> Scalar { + Scalar{ ls: [val, 0, 0, 0]} * R2 + } +} + +impl Scalar { + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + /// + /// **This operation is variable time with respect + /// to the exponent.** If the exponent is fixed, + /// this operation is effectively constant time. + pub fn pow_vartime(self, by: [u64; 4]) -> Scalar {//TODO implement when possible, this gives an error when called from sqrt + let mut res = ~Self::one(); + // let mut i = 4; + // while i > 0 { + // let e = by[i -1]; + // let mut j = 65; + // while j > 0 { + // res = res.square(); + + // if ((e >> (j-1)) & 1) == 1 { + // res *= self; + // // res.mul_assign(self); + // } + // j -= 1; + // } + // i -= 1; + // } + res + } +} + +impl Scalar { +/* +functions +- pow_vartime +- sqrt + +will give Immediate18TooLarge + +(It originally gave the error +error: Internal compiler error: Verification failed: Function anon_11103 return type must match its RET instructions. +Please file an issue on the repository and include the code that triggered this error. + +Sept 12: new error = +error: Internal compiler error: Verification failed: Function one_1 return type must match its RET instructions. +Please file an issue on the repository and include the code that triggered this error. +) +*/ + + /// Computes the square root of this element, if it exists. + pub fn sqrt(self) -> CtOption { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + + // w = self^((t - 1) // 2) + // = self^6104339283789297388802252303364915521546564123189034618274734669823 + let w = ~Scalar::one();/*pow_vartime(self, [ + 0x7fff_2dff_7fff_ffff, + 0x04d0_ec02_a9de_d201, + 0x94ce_bea4_199c_ec04, + 0x0000_0000_39f6_d3a9, + ]);*/ + + // let mut v = S; + // let mut x = self * w; + // let mut b = x * w; + + // // Initialize z as the 2^S root of unity. + // let mut z = ROOT_OF_UNITY; + + // let mut max_v = S; + + // while max_v > 0 { + // let mut k = 1; + // let mut tmp = b.square(); + // let mut j_less_than_v: Choice = ~Choice::from(1u8); + + // let mut j = 2; // j in 2..max_v + // while j <= max_v { + // let tmp_is_one = ~Choice::from_bool(tmp.eq(~Scalar::one())); + // let squared = ~Scalar::conditional_select(tmp, z, tmp_is_one).square(); + // tmp = ~Scalar::conditional_select(squared, tmp, tmp_is_one); + // let new_z = ~Scalar::conditional_select(z, squared, tmp_is_one); + // let j_less_than_v_bool = ~Choice::unwrap_as_bool(j_less_than_v) && j != v; + // j_less_than_v = ~Choice::from_bool(j_less_than_v_bool); + // k = ~u32::conditional_select(j, k, tmp_is_one); + // z = ~Scalar::conditional_select(z, new_z, j_less_than_v); + + // j += 1; + // } + + // let result = x * z; + // x = ~Scalar::conditional_select(result, x, ~Choice::from_bool(b.eq(~Scalar::one()))); + // z = z.square(); + // b *= z; + // v = k; + + // max_v -= 1; + // } + + // ~CtOption::new( + // x, + // (x * x).ct(self), // Only return Some if it's the square root. + // ) + ~CtOption::new(self, ~Choice::from(1)) + } +} + +impl Subtract for Scalar { + fn subtract(self, other: Self) -> Self { + self.sub(other) + } +} + +impl Multiply for Scalar { + fn multiply(self, other: Self) -> Self { + self.mul(other) + } +} \ No newline at end of file diff --git a/bls12_381/src/util.sw b/bls12_381/src/util.sw new file mode 100644 index 0000000..5c43af2 --- /dev/null +++ b/bls12_381/src/util.sw @@ -0,0 +1,125 @@ +library util; + +dep choice; + +use choice::{Choice, CtOption, ConditionallySelectable, wrapping_neg}; +use std::{u128::U128}; +use core::ops::{BitwiseXor}; +use core::num::*; +use std::flags::{disable_panic_on_overflow, enable_panic_on_overflow}; + + +impl ConditionallySelectable for u64 { + fn conditional_select(a: u64, b: u64, choice: Choice) -> u64 { + // if choice = 0, mask = (-0) = 0000...0000 + // if choice = 1, mask = (-1) = 1111...1111 + let choice_64: u64 = choice.unwrap_u8(); + let mask = wrapping_neg(choice_64); + b ^ (mask & (a ^ b)) + } +} + +impl BitwiseXor for u32 { + fn binary_xor(self, other: Self) -> Self { + asm(r1: self, r2: other, r3) { + xor r3 r1 r2; + r3: u32 + } + } +} + +impl ConditionallySelectable for u32 { + fn conditional_select(a: u32, b: u32, choice: Choice) -> u32 { + let choice_32: u32 = choice.unwrap_u8(); + let mask = wrapping_neg(choice_32); + b ^ (mask & (a ^ b)) + } +} + +// If input == 0u64, return 1. Otherwise return 0. +// Done in assembly, because natively comparison becomes a bool. +pub fn is_zero_u64(input: u64) -> u64 { + asm(r1: input, r2) { + eq r2 r1 zero; + r2: u64 + } +} + +// TODO rewrite without if branch. This one is used with variable a and b +// See resources below +// If x >= y: x-y, else max::U128 - (y-x) +pub fn subtract_wrap(x: U128, y: U128) -> U128 { + if y > x { + ~U128::max() - (y - x - U128 { + lower: 1, upper: 0 + }) + } else { + x - y + } +} + +// TODO rewrite without if branch +// In practice this is only being called with fixed values, so we can make it more specific. +// 1 -1 +// 0 -1 +// If x >= y: x-y, else max::U64 - (y-x) +pub fn subtract_wrap_64(x: u64, y: u64) -> u64 { + if y > x { + ~u64::max() - (y - x - 1) + } else { + x - y + } +} + +// +pub fn subtract_1_wrap(x: u64) -> u64 { + disable_panic_on_overflow(); + let res = asm(underflow, r1: x, r2, r3) { + subi r2 r1 i1; // x - 1 + move underflow of; // move the underflow to a variable + or r3 r2 underflow; // if 1-1 then this is 0 | 0 = 0, if 0-1 this is 0 | u64::max + r3 + }; + enable_panic_on_overflow(); + res +} + +/// Compute a - (b + borrow), returning the result and the new borrow. +pub fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + let borrow_128: U128 = ~U128::from(0, borrow >> 63); + + let res: U128 = subtract_wrap(a_128, b_128 + borrow_128); + (res.lower, res.upper) //(result, borrow) +} + +//returns sum with carry of a and b +pub fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + let carry_128: U128 = ~U128::from(0, carry); + + let sum = a_128 + b_128 + carry_128; + (sum.lower, sum.upper) +} + + +//returns the result and new carry of a + b*c + carry +pub fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + let c_128: U128 = ~U128::from(0, c); + let carry_128: U128 = ~U128::from(0, carry); + + let res: U128 = a_128 + (b_128 * c_128) + carry_128; + (res.lower, res.upper) +} + +//returns a*b mod(2^64) +pub fn wrapping_mul(a: u64, b: u64) -> u64 { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + + (a_128 * b_128).lower +} \ No newline at end of file diff --git a/bls12_381/tests/harness.rs b/bls12_381/tests/harness.rs new file mode 100644 index 0000000..120637e --- /dev/null +++ b/bls12_381/tests/harness.rs @@ -0,0 +1,31 @@ +use fuels::{prelude::*, tx::ContractId}; + +// Load abi from json +abigen!(MyContract, "out/debug/bls12-381-abi.json"); + +async fn get_contract_instance() -> (MyContract, ContractId) { + // Launch a local network and deploy the contract + let wallet = launch_provider_and_get_wallet().await; + + let id = Contract::deploy( + "./out/debug/bls12-381.bin", + &wallet, + TxParameters::default(), + StorageConfiguration::with_storage_path(Some( + "./out/debug/bls12-381-storage_slots.json".to_string(), + )), + ) + .await + .unwrap(); + + let instance = MyContract::new(id.to_string(), wallet); + + (instance, id) +} + +#[tokio::test] +async fn can_get_contract_id() { + let (_instance, _id) = get_contract_instance().await; + + // Now you have an instance of your contract you can use to test each function +} diff --git a/edwards25519/Cargo.toml b/edwards25519/Cargo.toml new file mode 100644 index 0000000..bd7cae6 --- /dev/null +++ b/edwards25519/Cargo.toml @@ -0,0 +1,16 @@ +[project] +name = "edwards25519" +version = "0.1.0" +authors = ["HashCloak"] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +fuels = "0.15" +fuels-abigen-macro = "0.15" +tokio = { version = "1.12", features = ["rt", "macros"] } + +[[test]] +harness = true +name = "integration_tests" +path = "tests/harness.rs" diff --git a/edwards25519/Forc.toml b/edwards25519/Forc.toml new file mode 100644 index 0000000..25dabbc --- /dev/null +++ b/edwards25519/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["HashCloak"] +entry = "field_element.sw" +license = "Apache-2.0" +name = "edwards25519" + +[dependencies] diff --git a/edwards25519/src/field_element.sw b/edwards25519/src/field_element.sw new file mode 100644 index 0000000..a22875e --- /dev/null +++ b/edwards25519/src/field_element.sw @@ -0,0 +1,476 @@ +library field_element; + +use std::u128::*; + +// Radix 51 representation of an integer: +// l0 + l1*2^51 + l2*2^102 + l3*2^153 + l4*2^204 +pub struct Element { + l0: u64, + l1: u64, + l2: u64, + l3: u64, + l4: u64, +} + +// = (1 << 51) - 1 +// But using the above expression gives the error "Could not evaluate initializer to a const declaration." +const MASK_LOW_51_BITS: u64 = 2251799813685247; + +pub const ZERO: Element = Element { + l0: 0, l1: 0, l2: 0, l3: 0, l4: 0 +}; + +// from NaCl impl https://cr.yp.to/ecdh.html#use +fn times19(x: u64) -> u64 { + (x << 4) + (x << 1) + x +} + +/*Do 1 round of carrying +This returns an element of which all li have at most 52 bits. +So the elm is at most: + (2^52 - 1) + + (2^52 - 1) * 2^51 + + (2^52 - 1) * 2^102 + + (2^52 - 1) * 2^153 + + (2^52 - 1) * 2^204 + */ +pub fn carry_propagate(e: Element) -> Element { + // all ci have at most 13 bits + let c0 = e.l0 >> 51; + let c1 = e.l1 >> 51; + let c2 = e.l2 >> 51; + let c3 = e.l3 >> 51; + // c4 represents what carries over to the 2^255 element + // since we're working 2^255 - 19, this will carry over to the first element, + // multiplied by 19 + let c4 = e.l4 >> 51; + + // c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and + // the final l0 will be at most 52 bits. Similarly for the rest. + + // c4 * 19 is at most 13 + 5 = 18 bits => l0 is at most 52 bits + let new_l0 = (e.l0 & MASK_LOW_51_BITS) + times19(c4); + Element { + l0: new_l0, + l1: (e.l1 & MASK_LOW_51_BITS) + c0, + l2: (e.l2 & MASK_LOW_51_BITS) + c1, + l3: (e.l3 & MASK_LOW_51_BITS) + c2, + l4: (e.l4 & MASK_LOW_51_BITS) + c3, + } +} + +/* +returns a carry element neq 0 if the e represents a number larger than 2^255 -19 +*/ +fn get_carry(e: Element) -> u32 { + let mut carry = times19(e.l0) >> 51; + carry = (e.l1 + carry) >> 51; + carry = (e.l2 + carry) >> 51; + carry = (e.l3 + carry) >> 51; + carry = (e.l4 + carry) >> 51; + carry +} + +/* +return reduced element mod 2^255-19 +*/ +pub fn reduce(e: Element) -> Element { + let mut red: Element = carry_propagate(e); + + //Determine whether *red* is already completely reduced mod 2^255-19 or not + // if v >= 2^255 - 19 => v + 19 >= 2^255 + let mut carry = (red.l0 + 19) >> 51; + carry = (red.l1 + carry) >> 51; + carry = (red.l2 + carry) >> 51; + carry = (red.l3 + carry) >> 51; + carry = (red.l4 + carry) >> 51; + + let mut v0 = red.l0 + times19(carry); + let mut v1 = red.l1 + (v0 >> 51); + v0 = v0 & MASK_LOW_51_BITS; + let mut v2 = red.l2 + (v1 >> 51); + v1 = v1 & MASK_LOW_51_BITS; + let mut v3 = red.l3 + (v2 >> 51); + v2 = v2 & MASK_LOW_51_BITS; + let mut v4 = (red.l4 + (v3 >> 51)) & MASK_LOW_51_BITS; + v3 = v3 & MASK_LOW_51_BITS; + + Element { + l0: v0, + l1: v1, + l2: v2, + l3: v3, + l4: v4, + } +} + +/* +return a + b mod 2^255 - 19 +*/ +pub fn add(a: Element, b: Element) -> Element { + let temp = Element { + l0: a.l0 + b.l0, + l1: a.l1 + b.l1, + l2: a.l2 + b.l2, + l3: a.l3 + b.l3, + l4: a.l4 + b.l4, + }; + + carry_propagate(temp) +} + +//subtract fn returns a - b mod 2^255 - 19 +pub fn subtract(a: Element, b: Element) -> Element { + // we add 2*p to avoid any underflow and then subtract b + let res: Element = Element { + l0: (a.l0 + 0xFFFFFFFFFFFDA) - b.l0, + l1: (a.l1 + 0xFFFFFFFFFFFFE) - b.l1, + l2: (a.l2 + 0xFFFFFFFFFFFFE) - b.l2, + l3: (a.l3 + 0xFFFFFFFFFFFFE) - b.l3, + l4: (a.l4 + 0xFFFFFFFFFFFFE) - b.l4, + }; + + reduce(res) +} + +//negate return negaive of an element(-a) mod 2^255 - 19 +pub fn negate(a: Element) -> Element { + subtract(ZERO, a) +} + +//returns 128-bit product of a and b +pub fn multiply64(a: u64, b: u64) -> U128 { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + a_128 * b_128 +} + +//returns sum with carry of a and b +pub fn add64(a: u64, b: u64, carry: u64) -> (u64, u64) { + let a_128: U128 = ~U128::from(0, a); + let b_128: U128 = ~U128::from(0, b); + let carry_128: U128 = ~U128::from(0, carry); + + let sum: u64 = (a_128 + b_128 + carry_128).lower; + let notSum = ~u64::max() - sum; + let carryOut = ((a & b) | ((a | b) & notSum)) >> 63; + + (sum, carryOut) +} + +//returns res + a * b +pub fn add_multiply64(res: U128, a: u64, b: u64) -> U128 { + let mul_res: U128 = multiply64(a, b); + let add_res: (u64, u64) = add64(mul_res.lower, res.lower, 0); + let add_res2: (u64, u64) = add64(mul_res.upper, res.upper, add_res.1); + + ~U128::from(add_res2.0, add_res.0) +} + +//right shift by 51 +pub fn shift_right_by51(a: U128) -> u64 { + (a.upper <<(64 - 51)) | (a.lower >> 51) +} + +//returns square of an Element +pub fn square(a: Element) -> Element { + multiply(a, a) +} + +//returns a*b +pub fn multiply(a: Element, b: Element) -> Element { + //https://cs.opensource.google/go/go/+/master:src/crypto/internal/edwards25519/field/fe_generic.go;l=34;bpv=0?q=feMul&sq=&ss=go%2Fgo + + let a0 = a.l0; + let a1 = a.l1; + let a2 = a.l2; + let a3 = a.l3; + let a4 = a.l4; + + let b0 = b.l0; + let b1 = b.l1; + let b2 = b.l2; + let b3 = b.l3; + let b4 = b.l4; + + let a1_19 = times19(a1); + let a2_19 = times19(a2); + let a3_19 = times19(a3); + let a4_19 = times19(a4); + + // r0 = a0ร—b0 + 19ร—(a1ร—b4 + a2ร—b3 + a3ร—b2 + a4ร—b1) + let mut r0: U128 = multiply64(a0, b0); + r0 = add_multiply64(r0, a1_19, b4); + r0 = add_multiply64(r0, a2_19, b3); + r0 = add_multiply64(r0, a3_19, b2); + r0 = add_multiply64(r0, a4_19, b1); + + // r1 = a0ร—b1 + a1ร—b0 + 19ร—(a2ร—b4 + a3ร—b3 + a4ร—b2) + let mut r1: U128 = multiply64(a0, b1); + r1 = add_multiply64(r1, a1, b0); + r1 = add_multiply64(r1, a2_19, b4); + r1 = add_multiply64(r1, a3_19, b3); + r1 = add_multiply64(r1, a4_19, b2); + + // r2 = a0ร—b2 + a1ร—b1 + a2ร—b0 + 19ร—(a3ร—b4 + a4ร—b3) + let mut r2: U128 = multiply64(a0, b2); + r2 = add_multiply64(r2, a1, b1); + r2 = add_multiply64(r2, a2, b0); + r2 = add_multiply64(r2, a3_19, b4); + r2 = add_multiply64(r2, a4_19, b3); + + // r3 = a0ร—b3 + a1ร—b2 + a2ร—b1 + a3ร—b0 + 19ร—a4ร—b4 + let mut r3: U128 = multiply64(a0, b3); + r3 = add_multiply64(r3, a1, b2); + r3 = add_multiply64(r3, a2, b1); + r3 = add_multiply64(r3, a3, b0); + r3 = add_multiply64(r3, a4_19, b4); + + //r4 = a0ร—b4 + a1ร—b3 + a2ร—b2 + a3ร—b1 + a4ร—b0 + let mut r4: U128 = multiply64(a0, b4); + r4 = add_multiply64(r4, a1, b3); + r4 = add_multiply64(r4, a2, b2); + r4 = add_multiply64(r4, a3, b1); + r4 = add_multiply64(r4, a4, b0); + + let c0 = shift_right_by51(r0); + let c1 = shift_right_by51(r1); + let c2 = shift_right_by51(r2); + let c3 = shift_right_by51(r3); + let c4 = shift_right_by51(r4); + + let rr0 = (r0.lower & MASK_LOW_51_BITS) + times19(c4); + let rr1 = (r1.lower & MASK_LOW_51_BITS) + c0; + let rr2 = (r2.lower & MASK_LOW_51_BITS) + c1; + let rr3 = (r3.lower & MASK_LOW_51_BITS) + c2; + let rr4 = (r4.lower & MASK_LOW_51_BITS) + c3; + + let res: Element = Element { + l0: rr0, + l1: rr1, + l2: rr2, + l3: rr3, + l4: rr4 + }; + + carry_propagate(res) +} + +// For a bignumber <= 102 bits stored in U128, +// return the 51 bit coefficient and 51 bit carry +pub fn get_coeff_and_carry(y: U128) -> (u64, u64) { + let coeff: u64 = y.lower & MASK_LOW_51_BITS; + let carry: u64 = (y.upper << 13 & MASK_LOW_51_BITS) | y.lower >> 51; + (coeff, carry) +} + +// returns e with all limbs multiplied by scalar x, reduced p +pub fn scalar_mult(e: Element, x: u32) -> Element { + let scalar_u128: U128 = ~U128::from(0, x); + + // e is radix 51, so all limbs have max 51 bits. The scalar has max 32 bits. + // Their multiplication has max 84 bits and is stored as (upper, lower) in U128 + let l0_temp: U128 = ~U128::from(0, e.l0) * scalar_u128; + let l1_temp: U128 = ~U128::from(0, e.l1) * scalar_u128; + let l2_temp: U128 = ~U128::from(0, e.l2) * scalar_u128; + let l3_temp: U128 = ~U128::from(0, e.l3) * scalar_u128; + let l4_temp: U128 = ~U128::from(0, e.l4) * scalar_u128; + + let (coeff0, carry0) = get_coeff_and_carry(l0_temp); + let (coeff1, carry1) = get_coeff_and_carry(l1_temp); + let (coeff2, carry2) = get_coeff_and_carry(l2_temp); + let (coeff3, carry3) = get_coeff_and_carry(l3_temp); + let (coeff4, carry4) = get_coeff_and_carry(l4_temp); + + let res0: u64 = coeff0 + times19(carry4); + let res1: u64 = coeff1 + carry0; + let res2: u64 = coeff2 + carry1; + let res3: u64 = coeff3 + carry2; + let res4: u64 = coeff4 + carry3; + + let res: Element = Element { + l0: res0, + l1: res1, + l2: res2, + l3: res3, + l4: res4 + }; + + res +} + +// Constant time equals +pub fn equals(a: Element, b: Element) -> bool { + let mut res: u64 = 0; + res = res + (a.l0 ^ b.l0); + res = res + (a.l1 ^ b.l1); + res = res + (a.l2 ^ b.l2); + res = res + (a.l3 ^ b.l3); + res = res + (a.l4 ^ b.l4); + + res == 0 +} + +/* +//Testing not done +//a^(-1) mod p = a^(p-2) mod p by Fermat's theorem, Hence we calculate a^(p-2) mod p +pub fn inverse(a: Element) -> Element { + let mut i = 0; + let z2 = square(a); //2 + let mut t = square(z2); //4 + t = square(t); //8 + +// Comment Elena: when I run inverse until here, it gives me the error: Immediate18TooLarge + + + // let z9 = multiply(t,a); //9 + // let z11 = multiply(z9, z2); //11 + // t = square(z11); //22 + + // let z2_5_0 = multiply(t, z9); //31 + // t = square(z2_5_0); // 62 = 2^6 - 2^1 + // while i < 4 { + // t = square(t); // 2^10 - 2^5 + // i+=1; + // } + + // let z2_10_0 = multiply(t, z2_5_0); // 2^10 - 2^0 + + // t = square(z2_10_0); //2^11 - 2^1 + // i = 0; + // while i < 9 { + // t = square(t); //2^20 - 2^10 + // i += 1; + // } + + // let z2_20_0 = multiply(t, z2_10_0); //2^20 - 2^0 + + // t = square(z2_20_0); //2^21 - 2^1 + // i = 0; + // while i < 19 { + // t = square(t); //2^40 - 2^20 + // i += 1; + // } + + // t = multiply(t, z2_20_0); //2^40 - 2^0 + + // t = square(t); // 2^41 - 2^1 + + // i = 0; + // while i < 10 { + // t = square(t); //2^50 - 2^10 + // } + + // let z2_50_0 = multiply(t, z2_10_0); //2^50 - 2^0 + + // t = square(z2_50_0); //2^51 - 2^1 + + // i = 0; + // while i < 49 { + // t = square(t); //2^100 - 2^50 + // i += 1; + // } + + // let z2_100_0 = multiply(t, z2_50_0);//2^100 - 2^0 + + // t = square(z2_100_0); //2^101 - 2^1 + + // i = 0; + // while i < 99 { + // t = square(t); //2^200 - 2^100 + // i += 1; + // } + + // t = multiply(t, z2_100_0); //2^200 - 2^0 + + // t = square(t); //2^201 - 2^1 + + // i = 0; + // while i < 49 { + // t = square(t); //2^250 - 2^50 + // i += 1; + // } + + // t = multiply(t, z2_50_0); //2^250 - 2^0 + + // i = 0; + // while i < 5 { + // t = square(t); //2^255 - 2^5 + // i += 1; + // } + + // t = multiply(t, z11); //2^255 - 21 = p - 2 + + return t; +} +*/ +// Invert sets v = 1/z mod p, and returns v. +// +// If z == 0, Invert returns v = 0. +/* +func (v *Element) Invert(z *Element) *Element { + // Inversion is implemented as exponentiation with exponent p โˆ’ 2. It uses the + // same sequence of 255 squarings and 11 multiplications as [Curve25519]. + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element + + z2.Square(z) // 2 + + t.Square(&z2) // 4 + + t.Square(&t) // 8 + + z9.Multiply(&t, z) // 9 + z11.Multiply(&z9, &z2) // 11 + t.Square(&z11) // 22 + z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 + + t.Square(&z2_5_0) // 2^6 - 2^1 + for i := 0; i < 4; i++ { + t.Square(&t) // 2^10 - 2^5 + } + z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 + + t.Square(&z2_10_0) // 2^11 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^20 - 2^10 + } + z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 + + t.Square(&z2_20_0) // 2^21 - 2^1 + for i := 0; i < 19; i++ { + t.Square(&t) // 2^40 - 2^20 + } + t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 + + t.Square(&t) // 2^41 - 2^1 + for i := 0; i < 9; i++ { + t.Square(&t) // 2^50 - 2^10 + } + z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 + + t.Square(&z2_50_0) // 2^51 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^100 - 2^50 + } + z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 + + t.Square(&z2_100_0) // 2^101 - 2^1 + for i := 0; i < 99; i++ { + t.Square(&t) // 2^200 - 2^100 + } + t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 + + t.Square(&t) // 2^201 - 2^1 + for i := 0; i < 49; i++ { + t.Square(&t) // 2^250 - 2^50 + } + t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 + + t.Square(&t) // 2^251 - 2^1 + t.Square(&t) // 2^252 - 2^2 + t.Square(&t) // 2^253 - 2^3 + t.Square(&t) // 2^254 - 2^4 + t.Square(&t) // 2^255 - 2^5 + + return v.Multiply(&t, &z11) // 2^255 - 21 +}*/ diff --git a/edwards25519/src/ge25519.sw b/edwards25519/src/ge25519.sw new file mode 100644 index 0000000..10e2f01 --- /dev/null +++ b/edwards25519/src/ge25519.sw @@ -0,0 +1,68 @@ +library ge25519; + +use field_element::*; + +/* Point representation with extended coordinates, they satisfy: + x=X/Z + y=Y/Z + x*y=T/Z + See https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html +*/ +pub struct Ge25519 { + x: Element, + y: Element, + z: Element, + t: Element, +} + +// Completed point (https://doc-internal.dalek.rs/curve25519_dalek/backend/serial/curve_models/index.html) +pub struct Ge25519_p1p1 { + x: Element, + y: Element, + z: Element, + t: Element, +} + +/* Projective point. Satisfies: + x=X/Z + y=Y/Z +- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-projective.html +- https://doc-internal.dalek.rs/curve25519_dalek/backend/serial/curve_models/index.html +*/ +pub struct Ge25519_p2 { + x: Element, + y: Element, + z: Element, +} + +pub struct Ge25519_aff { + x: Element, + y: Element, +} + +/* +http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd +*/ +// TODO TEST! +pub fn double(p: Ge25519_p2) -> Ge25519_p1p1 { + let a = square(p.x); + let b = square(p.y); + let mut c = square(p.z); + c = add(c, c); + let d = negate(a); + + let mut res_x = add(p.x, p.y); + res_x = square(res_x); + res_x = subtract(res_x, a); + res_x = subtract(res_x, b); + let res_z = add(d, b); + let res_t = subtract(res_z, c); + let res_y = subtract(d, b); + + Ge25519_p1p1 { + x: res_x, + y: res_y, + z: res_Z, + t: res_t, + } +} diff --git a/edwards25519/src/main.sw b/edwards25519/src/main.sw new file mode 100644 index 0000000..5032bbb --- /dev/null +++ b/edwards25519/src/main.sw @@ -0,0 +1,1287 @@ +script; + +dep field_element; +dep test_helpers; + +use ::field_element::*; +use ::test_helpers::*; + +use std::{assert::assert, option::*, vec::Vec}; +use std::logging::log; +use core::num::*; +use std::u128::*; + + +fn main() { + // THESE TESTS ARE ALL OK + assert(test_helpers()); + // assert(test_reductions()); + // assert(tests_add()); + // assert(tests_scalar_mult()); + // assert(tests_multiply64()); + // assert(tests_add64()); + // assert(tests_add_multiply64()); + // assert(tests_shiftRightBy51()); + // assert(tests_substract()); + + // Multiply functions only test OK if ran 1 at a time. (?!) + // assert(tests_multiply()); + // assert(tests_square()); + // assert(tests_inverse()); + + +} + +fn test_helpers() -> bool { + assert(test_get_zero()); + assert(test_equals()); + true +} + +fn test_get_zero() -> bool { + res_equals(zero, Element{ l0: 0, l1: 0, l2: 0, l3: 0, l4: 0 }); + true +} + +fn test_equals() -> bool { + let zero_equals_zero = equals(zero, zero); + let zero_equals_one = equals(zero, one); + let one_equals_one = equals(one, one); + + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + let b = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + let c = Element{ + l0: 60, + l1: 5, + l2: 2251799813685247, + l3: 500, + l4: 100 + }; + let a_equals_a = equals(a,a); + let a_equals_b = equals(a,b); //a and b have same coefficients + let a_equals_c = equals(a,c); + let b_equals_c = equals(b,c); + + assert(zero_equals_zero); + assert(!zero_equals_one); + assert(one_equals_one); + + assert(a_equals_a); + assert(a_equals_b); + assert(!a_equals_c); + assert(!b_equals_c); + + true +} +//------------------------------------------------------------------------------------------------ +fn test_reductions() -> bool { + assert(test_carry_propagate_1()); + assert(test_carry_propagate_2()); + assert(test_reduce()); + assert(test_reduce_2()); + assert(test_reduce_3()); + assert(test_reduce_4()); + true +} + +fn test_carry_propagate_1() -> bool { + /* + 2^64 -1 = 18446744073709551615 + + 18446744073709551615 + + 18446744073709551615 * 2^51 + + 18446744073709551615 * 2^102 + + 18446744073709551615 * 2^153 + + 18446744073709551615 * 2^204 + */ + let e = Element{ + l0: ~u64::max(), + l1: ~u64::max(), + l2: ~u64::max(), + l3: ~u64::max(), + l4: ~u64::max() }; + + let res = carry_propagate(e); + + // Each limb has a carry of 8191 and a coeff of 2251799813685247 + // So after a round of reduction this is + /* equals + 2251799813685247 + (19*8191) + + (2251799813685247 + 8191) * 2^51 + + (2251799813685247 + 8191) * 2^102 + + (2251799813685247 + 8191) * 2^153 + + (2251799813685247 + 8191) * 2^204 + = 57896044618868696584113898827420068118245036358148060739095062128926159691756 + */ + res_equals(res, Element{ + l0: 2251799813685247 + (19*8191), + l1: 2251799813685247 + 8191, + l2: 2251799813685247 + 8191, + l3: 2251799813685247 + 8191, + l4: 2251799813685247 + 8191 + }); + + // Note that this result is >2^255-19 because the function only does 1 round of reduction + + true +} + +fn test_carry_propagate_2() -> bool { + // 2251799813685250 = 2 + 2^51 + let e = Element{ + l0: 2251799813685250, + l1: 0, + l2: 0, + l3: 0, + l4: 0 }; + + let res = carry_propagate(e); + + res_equals(res, Element{ l0: 2, l1: 1, l2: 0, l3: 0, l4: 0 }); + + true +} + +fn test_reduce() -> bool { + // 2251799813685250 = 2 + 2^51 + let e = Element{ + l0: 2251799813685250, + l1: 0, + l2: 0, + l3: 0, + l4: 0 }; + + let res = reduce(e); + + res_equals(res, Element{ l0: 2, l1: 1, l2: 0, l3: 0, l4: 0 }); + + true +} + +fn test_reduce_2() -> bool { + /* + 2^51 + 2 = 2251799813685250 + + 2251799813685250 + + 2251799813685250 * 2^51 + + 2251799813685250 * 2^102 + + 2251799813685250 * 2^153 + + 2251799813685250 * 2^204 + mod 2^255-19 + */ + let e = Element{ + l0: 2251799813685250, + l1: 2251799813685250, + l2: 2251799813685250, + l3: 2251799813685250, + l4: 2251799813685250 }; + + let res = reduce(e); + + /* equals + 21 + + 3 * 2^51 + + 3 * 2^102 + + 3 * 2^153 + + 3 * 2^204 + */ + res_equals(res, Element{ l0: 21, l1: 3, l2: 3, l3: 3, l4: 3 }); + + true +} + +fn test_reduce_3() -> bool { + /* + 2^64 -1 = 18446744073709551615 + + 18446744073709551615 + + 18446744073709551615 * 2^51 + + 18446744073709551615 * 2^102 + + 18446744073709551615 * 2^153 + + 18446744073709551615 * 2^204 + mod 2^255 -19 + = 210598872328406323076114191610044025327778719366270124969594871807 + */ + let e = Element{ + l0: ~u64::max(), + l1: ~u64::max(), + l2: ~u64::max(), + l3: ~u64::max(), + l4: ~u64::max() }; + + let res = reduce(e); + + /* + 155647 + + 8191 * 2^51 + + 8191 * 2^102 + + 8191 * 2^153 + + 8191 * 2^204 + = 210598872328406323076114191610044025327778719366270124969594871807 + */ + res_equals(res, Element{ + l0: 155647, + l1: 8191, + l2: 8191, + l3: 8191, + l4: 8191 + }); + + true +} + +fn test_reduce_4() -> bool { + /* + 4503599627370494 + + 4503599627370494 * 2^51 + + 4503599627370494 * 2^102 + + 4503599627370494 * 2^153 + + 4503599627370494 * 2^204 + mod 2^255 - 19 + + = 36 + */ + let e = Element{ + l0: 4503599627370494, + l1: 4503599627370494, + l2: 4503599627370494, + l3: 4503599627370494, + l4: 4503599627370494 }; + + let res = reduce(e); + + /* + should be 36 + */ + res_equals(res, Element{ + l0: 36, + l1: 0, + l2: 0, + l3: 0, + l4: 0 + }); + + true +} +//------------------------------------------------------------------------------------------- +fn tests_add() -> bool { + assert(test_add_to_0()); + assert(test_add_0()); + assert(test_add_a_to_b()); + assert(test_add_a_to_a()); + true + +} + +fn test_add_to_0() -> bool { + let b = Element{ + l0: 8191, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }; + + let res = add(zero, b); + + assert(res_equals(res, b)); + true +} + +fn test_add_0() -> bool { + let b = Element{ + l0: 8191, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }; + + let res = add(b, zero); + + assert(res_equals(res, b)); + true +} + +fn test_add_a_to_b() -> bool { + // coefficients are 2^51-1 + /* + 2251799813685247 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 2251799813685247 * 2^153 + + 2251799813685247 * 2^204 + = 57896044618658097711785492504343953926634992332820282019728792003956564819967 + */ + let a = Element{ + l0: 2251799813685247, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + // random + /* + 8191 + + 225179 * 2^51 + + 155647 * 2^102 + + 81918191 * 2^153 + + 85247 * 2^204 + = 2191786359344073644698773448800701597704682582429191096092436996095 + */ + let b = Element{ + l0: 8191, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }; + + // a+b mod 2^255 -19 + // should be 2191786359344073644698773448800701597704682582429191096092436996113 + let res = add(a,b); + + /* + 8209 + + 225179 * 2^51 + + 155647 * 2^102 + + 81918191 * 2^153 + + 85247 * 2^204 + = 2191786359344073644698773448800701597704682582429191096092436996113 + */ + res_equals(res, Element{ + l0: 8209, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }); + + true +} + +fn test_add_a_to_a() -> bool { + // 2^255 - 20 (the largest number) expressed in radix-51 + /* + 2251799813685228 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 2251799813685247 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 2251799813685228, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + let res = add(a, a); + + res_equals(res, Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }); + + true +} +//-------------------------------------------------------------------------------------------- +fn tests_scalar_mult() -> bool { + assert(test_mult_by_0()); + assert(test_mult_by_1()); + assert(test_mult_by_2()); + assert(test_mult_by_2_again()); + assert(test_mult_by_large_scalar()); + true +} + +fn test_mult_by_0() -> bool { + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let res: Element = scalar_mult(a, 0); + res_equals(res, zero); + true +} + +fn test_mult_by_1() -> bool { + let a = Element{ + l0: 79611, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 555555333333222, + l4: 2251799813685247 + }; + + let res: Element = scalar_mult(a, 1); + res_equals(res, a); + true +} + +fn test_mult_by_2() -> bool { + /* + 79611 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 555555333333222 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 79611, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 555555333333222, + l4: 2251799813685247 + }; + + /* 57896044618658058976409158941982667984400415217654165675094330160006532591113 + =[159241, + 2251799813685246, + 2251799813685247, + 1111110666666445, + 2251799813685246] */ + let res: Element = scalar_mult(a, 2); + + res_equals(res, Element{ + l0: 159241, + l1: 2251799813685246, + l2: 2251799813685247, + l3: 1111110666666445, + l4: 2251799813685246 + }); + + true +} + +fn test_mult_by_2_again() -> bool { + // 2^255 - 20 (the largest number) expressed in radix-51 + /* + 2251799813685228 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 2251799813685247 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 2251799813685228, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + + let res: Element = scalar_mult(a, 2); + + /* + 2251799813685227 + 2251799813685247* 2^51 + + 2251799813685247* 2^102 + + 2251799813685247* 2^153 + + 2251799813685247 * 2^204 + */ + res_equals(res, Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }); + + true +} + +fn test_mult_by_large_scalar() -> bool { + /* + 79611 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 555555333333222 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 79611, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 555555333333222, + l4: 2251799813685247 + }; + + /* + 248661618146997193275445770277948644497863737508141907291226725252005565483705230608645 + reduced mod 2^255 - 19 + = 57895961435070841628109209456323569962738033236635300844883400985433276860639 + = [342008245700831, 2251795518717953, 2251799813685247, 536152338865944, 2251796578355658] + */ + // Largest scalar input possible is 2^32 -1 = 4294967295 + let res: Element = scalar_mult(a, 4294967295); + + res_equals(res, Element{ + l0: 342008245700831, + l1: 2251795518717953, + l2: 2251799813685247, + l3: 536152338865944, + l4: 2251796578355658 + }); + + true +} +//-------------------------------------------------------------------------------------------- +fn tests_substract() -> bool { + assert(test_subtraction_by_0()); + assert(test_subtraction_by_1()); + assert(test_subtraction_by_max()); + assert(test_subtraction_random()); + true +} + +fn test_subtraction_by_0() -> bool { + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let res: Element = subtract(a, zero); + res_equals(res, a); + + true +} + +fn test_subtraction_by_1() -> bool { + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let b = Element{ + l0: 2251799813685246, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let res: Element = subtract(a, one); + res_equals(res, b); + true +} + +fn test_subtraction_by_max() -> bool { + + //using GP-PARI one can convert a number into arbitrary base using the command digits(number, base) + + /* + 2^255 - 21 = 57896044618658097711785492504343953926634992332820282019728792003956564819947 + = [2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685227] + = Element { + 2251799813685227, + 2251799813685247, + 2251799813685247, + 2251799813685247, + 2251799813685247 + } + */ + + let a = Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + /* + 2^255 - 20 = 57896044618658097711785492504343953926634992332820282019728792003956564819948 + = [2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685228] + = Element { + 2251799813685228, + 2251799813685247, + 2251799813685247, + 2251799813685247, + 2251799813685247 + } + */ + + let b = Element { + l0: 2251799813685228, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + let res: Element = subtract(a, b); + let res2: Element = subtract(b,a); + res_equals(res, b); + res_equals(res2, one); + true +} + +fn test_subtraction_random() -> bool { + /* + subtraction of random 2 numbers generated using GP-PARI + + a = random({2^251}) = 1300760531839662334344262085631565818852980666446405835776058138544728770104 + = [50591579140481, 601879629470779, 595911506101250, 1303372017735434, 1292655137982008] + = Element { + 1292655137982008, + 1303372017735434, + 595911506101250, + 601879629470779, + 50591579140481 + } + + b = random({2^251}) = 3527794837033309378261417350654351403080646879795459845564282655359926745351 + = [137209507300112, 293961277766182, 335483569739384, 807899991388824, 1360902863141127] + = Element { + 1360902863141127, + 807899991388824, + 335483569739384, + 293961277766182, + 137209507300112 + } + + b - a = 2227034305193647043917155265022785584227666213349054009788224516815197975247 + = [86617928159630, 1943881461980650, 1991371877323381, 1756327787338638, 68247725159119] + = Element { + 68247725159119, + 1756327787338638, + 1991371877323381, + 1943881461980650, + 86617928159630 + } + a - b + p = 55669010313464450667868337239321168342407326119471228009940567487141366844702 + = [2165181885525617, 307918351704597, 260427936361866, 495472026346609, 2183552088526110] + = Element { + 2183552088526110, + 495472026346609, + 260427936361866, + 307918351704597, + 2165181885525617 + } + */ + + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; + + let b = Element { + l0: 1360902863141127, + l1: 807899991388824, + l2: 335483569739384, + l3: 293961277766182, + l4: 137209507300112 + }; + + let a_minus_b = Element { + l0: 2183552088526110, + l1: 495472026346609, + l2: 260427936361866, + l3: 307918351704597, + l4: 2165181885525617 + }; + + let b_minus_a = Element { + l0: 68247725159119, + l1: 1756327787338638, + l2: 1991371877323381, + l3: 1943881461980650, + l4: 86617928159630 + }; + + let res: Element = subtract(a, b); + let res2: Element = subtract(b,a); + res_equals(res, a_minus_b); + res_equals(res2, b_minus_a); + //print_el(res2); + true +} +//------------------------------------------------------------------------------------------------------------- +fn tests_multiply64() -> bool { + assert(test_multiply64_random()); + true +} + +fn test_multiply64_random()-> bool{ + let a = 9837491998535547791; + let b = 10009796384580774444; +/* +a*b = 98471291840283423519614919326553453204 + = [5338139427034470684, 5960040633016627860] +*/ + let ab: U128 = U128 { + upper: 5338139427034470684, + lower: 5960040633016627860 + }; + + let res = multiply64(a, b); + assert(res == ab); + //print_U128(res); + + true +} +//---------------------------------------------------------------------------------------------------- +fn tests_add64() -> bool { + assert(test_add64_random()); + assert(test_add64_random_with_carry()); + true +} + +fn test_add64_random() -> bool { + let a = 9837491998535547791; + let b = 10009796384580774444; + let res:(u64,u64) = add64(a, b, 0); + let a_plus_b: (u64,u64) = (1400544309406770619, 1); + + assert(res.0 == a_plus_b.0); + assert(res.1 == a_plus_b.1); + true +} + +fn test_add64_random_with_carry() -> bool { + let a = 9837491998535547791; + let b = 10009796384580774444; + let res:(u64,u64) = add64(a, b, 1); + let a_plus_b_and_carry: (u64,u64) = (1400544309406770620, 1); + + assert(res.0 == a_plus_b_and_carry.0); + assert(res.1 == a_plus_b_and_carry.1); + true +} +//------------------------------------------------------------------------------------------------------------ +fn tests_add_multiply64() -> bool { + assert(test_add_multiply64()); + assert(test_add_multiply64_2()); + // assert(test_add_multiply64_random()); + // assert(test_add_multiply64_max()); + true +} + +fn test_add_multiply64() -> bool { + let a = 496009164746885; + let b = 24764068336973246; + //ab=12283204851556881218686606838710 + + let r = U128{upper: 2516888776885, lower: 8614063320694916486}; + //r=46428403129198069714856710112646 + + // should be + //58711607980754950933543316951356 + //[10881738262824685884, 3182762646142] + let mut res = add_multiply64(r,a,b); + equals_U128(res, 10881738262824685884, 3182762646142); + + true +} + +fn test_add_multiply64_2() -> bool { + let a = 24764068336973246; + let b = 137209507300112; + //ab=3397865615262403032595436803552 + + // r= 1759178078333803271346890843016 + //[16956620749643293576, 95365234715] + let r = U128{upper: 95365234715, lower: 16956620749643293576}; + + // should be + //5157043693596206303942327646568 + //[18148778710141221224, 279563898809] + let mut res = add_multiply64(r,a,b); + + equals_U128(res, 18148778710141221224, 279563898809); + + true +} + +fn test_add_multiply64_max() -> bool { + /* + random 51 bit number a = 496009164746885 + a*19 = 9424174130190815 + random 107 bit number r = 46428403129198069714856710112646 + + res = [2770292668654, 9024291134538729057] + */ + + let a = 496009164746885; + let b = a*19; + let r = U128{upper: 2516888776885, lower: 8614063320694916486}; + + let mut res = add_multiply64(r,a,b); + // res = add_multiply64(r,a,b); + // res = add_multiply64(r,a,b); + // res = add_multiply64(r,a,b); + // res = add_multiply64(r,a,b); + print_U128(res); + //5338139427034470685 + //16558383139134563912 + true + +} + +fn test_add_multiply64_random() -> bool { + let a = 9837491998535547791; + let b = 10009796384580774444; + /* + a*b = 98471291840283423519614919326553453204 + = [5338139427034470684, 5960040633016627860] + + random r = 10598342506117936052 + + ab + r = 98471291840283423530213261832671389256 + = [5338139427034470684, 16558383139134563912] + + */ + let r:U128 = U128{upper: 0, lower: 10598342506117936052}; + let res = add_multiply64(r, a, b); + + print_U128(res); +//2770292668654 +//9024291134538729057 + true + + + +} +//----------------------------------------------------------------------------------------------------- + +fn tests_shiftRightBy51() -> bool { + assert(test_shiftRightBy51_random()); + assert(test_shiftRightBy51_random_2()); + assert(test_shiftRightBy51_random_3()); + true +} + +fn test_shiftRightBy51_random() -> bool { + + let a = U128{upper: 16, lower:0}; + let res = shiftRightBy51(a); + assert(res == 131072); + true +} + +fn test_shiftRightBy51_random_2() -> bool { + /* +456464 + (349323232 << 64) = 6443876259705066799772399376 + +>>51 = 2861655916544 + */ + let a = U128{upper: 349323232, lower:456464}; + let res = shiftRightBy51(a); + assert(res == 2861655916544); + true +} + +fn test_shiftRightBy51_random_3() -> bool { + /* +18446744073709551615 + (349323232 << 64) = 6443876278151810873481494527 + +>>51 = 2861655924735 + */ + let a = U128{upper: 349323232, lower:18446744073709551615}; + let res = shiftRightBy51(a); + assert(res == 2861655924735); + true +} + +//---------------------------------------------------------------------------------------------------- +fn tests_multiply() -> bool { + // assert(test_multiply_by_0()); + // assert(test_multiply_1_by_1()); + // assert(test_multiply_by_1_small()); + // assert(test_multiply_by_1_large()); + // assert(test_multiply_small_elms()); + // assert(test_multiply_small_elms_2()); + // assert(test_multiply_small_elms_3()); + assert(test_multiply_elms_4()); + // assert(test_multiply_random()); + true +} + +fn test_multiply_by_0() -> bool { + + //a = 2^255 - 21 + let a = Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + let res: Element = multiply(a, zero); + res_equals(res, zero); + true +} + +fn test_multiply_1_by_1() -> bool { + let res: Element = multiply(one, one); + res_equals(res, one); + + true +} + +fn test_multiply_by_1_small() -> bool { + + let a = Element{ + l0: 10, + l1: 11, + l2: 12, + l3: 13, + l4: 14 + }; + let res: Element = multiply(a, one); + res_equals(res, a); + true +} + +fn test_multiply_by_1_large() -> bool { + + //a = 2^255 - 21 + let a = Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + let res: Element = multiply(a, one); + res_equals(res, a); + true +} + +fn test_multiply_small_elms() -> bool { + +/* +10 + +11 * 2^51 + +12 * 2^102 + +13 * 2^153 + +14 * 2^204 += +359954121914013970155159550104308900255525248390881418617552906 +*/ + let a = Element{ + l0: 10, + l1: 11, + l2: 12, + l3: 13, + l4: 14 + }; +/* +2 + +3 * 2^51 + +4 * 2^102 + +5 * 2^153 + +6 * 2^204 += +154266052248863123541936069103167134583173917060132914191138818 +*/ + let b = Element{ + l0: 2, + l1: 3, + l2: 4, + l3: 5, + l4: 6 + }; + let res: Element = multiply(a, b); + + // = 5913532002873104218298081466563921672602110506934768320078942312 + // = [4200, 3719, 2909, 1752, 230] + res_equals(res, Element { + l0: 4200, + l1: 3719, + l2: 2909, + l3: 1752, + l4: 230 + }); + true +} + +fn test_multiply_small_elms_2() -> bool { + +/* +10 + +11 * 2^51 + +12 * 2^102 + +1292655137982008 * 2^153 + +14 * 2^204 += +374713634419208422705277081530098872100895088375962880689831946 +*/ + let a = Element{ + l0: 10, + l1: 11, + l2: 12, + l3: 1292655137982008, + l4: 14 + }; +/* +2 + +3 * 2^51 + +4 * 2^102 + +5 * 2^153 + +6 * 2^204 += +154266052248863123541936069103167134583173917060132914191138818 +*/ + let b = Element{ + l0: 2, + l1: 3, + l2: 4, + l3: 5, + l4: 6 + }; + let res: Element = multiply(a, b); + +//41810357909193713957696946830561922437378062290049323977762016618127807079743 +//[1414398498170175, 1205048169289895, 995697840409273, 333510462280559, 1626165600260968] + res_equals(res, Element { + l0: 1414398498170175, + l1: 1205048169289895, + l2: 995697840409273, + l3: 333510462280559, + l4: 1626165600260968 + }); + true +} + +fn test_multiply_small_elms_3() -> bool { + +/* +1292655137982008 + +1303372017735434 * 2^51 + +595911506101250 * 2^102 + +601879629470779 * 2^153 + +50591579140481 * 2^204 += +1300760531839662334344262085631565818852980666446405835776058138544728770104 +*/ + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; +/* +2 + +3 * 2^51 + +4 * 2^102 + +5 * 2^153 + +6 * 2^204 += +154266052248863123541936069103167134583173917060132914191138818 +*/ + let b = Element{ + l0: 2, + l1: 3, + l2: 4, + l3: 5, + l4: 6 + }; + let res: Element = multiply(a, b); + +//14111756534184466847966592306453182667710228607558444947423180242606813803383 +//[1954506281775991, 334157138245186, 376444288997219, 169499236944723, 548860478185542] res_equals(res, Element { + res_equals(res, Element { + l0: 1954506281775991, + l1: 334157138245186, + l2: 376444288997219, + l3: 169499236944723, + l4: 548860478185542 + }); + true +} + +fn test_multiply_elms_4() -> bool { + +/* +1292655137982008 + +1303372017735434 * 2^51 + +595911506101250 * 2^102 + +601879629470779 * 2^153 + +50591579140481 * 2^204 += +1300760531839662334344262085631565818852980666446405835776058138544728770104 +*/ + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; +/* +1360902863141127 + +807899991388824 * 2^51 + +335483569739384 * 2^102 + +293961277766182 * 2^153 + +137209507300112 * 2^204 += +3527794837033309378261417350654351403080646879795459845564282655359926745351 +*/ + let b = Element { + l0: 1360902863141127, + l1: 807899991388824, + l2: 335483569739384, + l3: 293961277766182, + l4: 137209507300112 + }; + let res: Element = multiply(a, b); + +//26419211797770492947925431317169526880133696901606348938375383866077151145114 +//[896638975913114, 1000789340506524, 355992668009873, 806477788321681, 1027544741541094] + res_equals(res, Element { + l0: 896638975913114, + l1: 1000789340506524, + l2: 355992668009873, + l3: 806477788321681, + l4: 1027544741541094 + }); + true +} + + +fn test_multiply_random() -> bool { + /* + a = random({2^251}) = 1300760531839662334344262085631565818852980666446405835776058138544728770104 + = [1292655137982008, 1303372017735434, 595911506101250, 601879629470779, 50591579140481] + = Element { + 1292655137982008, + 1303372017735434, + 595911506101250, + 601879629470779, + 50591579140481 + } + + b = random({2^251}) = 3527794837033309378261417350654351403080646879795459845564282655359926745351 + = [1360902863141127, 807899991388824, 335483569739384, 293961277766182, 137209507300112] + = Element { + 1360902863141127, + 807899991388824, + 335483569739384, + 293961277766182, + 137209507300112 + } + + ab mod p = 26419211797770492947925431317169526880133696901606348938375383866077151145114 + = [896638975913114, 1000789340506524, 355992668009873, 806477788321681, 1027544741541094] + + */ + + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; + + let b = Element { + l0: 1360902863141127, + l1: 807899991388824, + l2: 335483569739384, + l3: 293961277766182, + l4: 137209507300112 + }; + + let ab = Element { + l0: 896638975913114, + l1: 1000789340506524, + l2: 355992668009873, + l3: 806477788321681, + l4: 1027544741541094 + }; + + + let res: Element = multiply(a, b); + // print_el(res); + res_equals(res, ab); + true +} + +//--------------------------------------------------------------------------------------------------------- + +fn tests_square() -> bool { + // assert(test_square_one()); + // assert(test_square_zero()); + assert(test_square_random()); + true +} + +fn test_square_one() -> bool { + let res: Element = square(one); + res_equals(res, one); + true +} + +fn test_square_zero() -> bool { + let res: Element = square(zero); + res_equals(res, zero); + true +} + +fn test_square_random() -> bool { + /* + a = 10406084254717298682985401286246103758749877058048555650117556132341049525816 + = [404732633123850, 312158315551803, 595911506101250, 1303372017735434, 1292655137982008] + + a^2 = 108286589516275277577373161473966445398256506358898379115846293425853328039181187715348880647692926795184739504006825065723361671067040986483318450465856 + + a^2 mod p = 31042580993865580304222384966530826020546840941977848835924726765219788840807 + = [1207365348681671, 713663780369466, 912635275234964, 596790797966485, 2144628324130663] + + */ + + let a: Element = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 312158315551803, + l4: 404732633123850 + }; + + let a_square: Element = Element { + l0: 2144628324130663, + l1: 596790797966485, + l2: 912635275234964, + l3: 713663780369466, + l4: 1207365348681671 + }; + + let res: Element = square(a); + res_equals(res, a_square); + true +} + diff --git a/edwards25519/src/test_helpers.sw b/edwards25519/src/test_helpers.sw new file mode 100644 index 0000000..aa91f0c --- /dev/null +++ b/edwards25519/src/test_helpers.sw @@ -0,0 +1,35 @@ +library test_helpers; + +dep field_element; + +use field_element::*; +use std::logging::log; +use std::assert::assert; +use std::u128::*; + +pub const ONE: Element = Element { + l0: 1, l1: 0, l2: 0, l3: 0, l4: 0 +}; + +pub fn print_el(e: Element) { + log(e.l0); + log(e.l1); + log(e.l2); + log(e.l3); + log(e.l4); +} + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert(res.l0 == should_be.l0); + assert(res.l1 == should_be.l1); + assert(res.l2 == should_be.l2); + assert(res.l3 == should_be.l3); + assert(res.l4 == should_be.l4); + true +} + +pub fn equals_u128(res: U128, lower: u64, upper: u64) -> bool { + assert(res.upper == upper); + assert(res.lower == lower); + true +} diff --git a/edwards25519/src/tests/tests_add.sw b/edwards25519/src/tests/tests_add.sw new file mode 100644 index 0000000..9a4cece --- /dev/null +++ b/edwards25519/src/tests/tests_add.sw @@ -0,0 +1,129 @@ +library tests_add; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; +use std::u128::*; + +pub fn tests_add() -> bool { + assert(test_add_to_0()); + assert(test_add_0()); + assert(test_add_a_to_b()); + assert(test_add_a_to_a()); + true + +} + +fn test_add_to_0() -> bool { + let b = Element{ + l0: 8191, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }; + + let res = add(ZERO, b); + + assert(res_equals(res, b)); + true +} + +fn test_add_0() -> bool { + let b = Element{ + l0: 8191, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }; + + let res = add(b, ZERO); + + assert(res_equals(res, b)); + true +} + +fn test_add_a_to_b() -> bool { + // coefficients are 2^51-1 + /* + 2251799813685247 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 2251799813685247 * 2^153 + + 2251799813685247 * 2^204 + = 57896044618658097711785492504343953926634992332820282019728792003956564819967 + */ + let a = Element{ + l0: 2251799813685247, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + // random + /* + 8191 + + 225179 * 2^51 + + 155647 * 2^102 + + 81918191 * 2^153 + + 85247 * 2^204 + = 2191786359344073644698773448800701597704682582429191096092436996095 + */ + let b = Element{ + l0: 8191, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }; + + // a+b mod 2^255 -19 + // should be 2191786359344073644698773448800701597704682582429191096092436996113 + let res = add(a,b); + + /* + 8209 + + 225179 * 2^51 + + 155647 * 2^102 + + 81918191 * 2^153 + + 85247 * 2^204 + = 2191786359344073644698773448800701597704682582429191096092436996113 + */ + res_equals(res, Element{ + l0: 8209, + l1: 225179, + l2: 155647, + l3: 81918191, + l4: 85247 + }) +} + +fn test_add_a_to_a() -> bool { + // 2^255 - 20 (the largest number) expressed in radix-51 + /* + 2251799813685228 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 2251799813685247 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 2251799813685228, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + let res = add(a, a); + + res_equals(res, Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }) +} \ No newline at end of file diff --git a/edwards25519/src/tests/tests_helpers64.sw b/edwards25519/src/tests/tests_helpers64.sw new file mode 100644 index 0000000..727946a --- /dev/null +++ b/edwards25519/src/tests/tests_helpers64.sw @@ -0,0 +1,98 @@ +library tests_helpers64; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; +use std::u128::*; + +pub fn test_helpers64() -> bool { + assert(tests_multiply64()); + assert(tests_add64()); + assert(tests_add_multiply64()); + true +} + + +fn tests_multiply64() -> bool { + let a = 9837491998535547791; + let b = 10009796384580774444; +/* +a*b = 98471291840283423519614919326553453204 + = [5338139427034470684, 5960040633016627860] +*/ + let ab: U128 = U128 { + upper: 5338139427034470684, + lower: 5960040633016627860 + }; + + let res = multiply64(a, b); + assert(res == ab); + + true +} + +fn tests_add64() -> bool { + assert(test_add64_random()); + assert(test_add64_random_with_carry()); + true +} + +fn test_add64_random() -> bool { + let a = 9837491998535547791; + let b = 10009796384580774444; + let res:(u64,u64) = add64(a, b, 0); + let a_plus_b: (u64,u64) = (1400544309406770619, 1); + + assert(res.0 == a_plus_b.0); + assert(res.1 == a_plus_b.1); + true +} + +fn test_add64_random_with_carry() -> bool { + let a = 9837491998535547791; + let b = 10009796384580774444; + let res:(u64,u64) = add64(a, b, 1); + let a_plus_b_and_carry: (u64,u64) = (1400544309406770620, 1); + + assert(res.0 == a_plus_b_and_carry.0); + assert(res.1 == a_plus_b_and_carry.1); + true +} + +fn tests_add_multiply64() -> bool { + assert(test_add_multiply64()); + assert(test_add_multiply64_2()); + true +} + +fn test_add_multiply64() -> bool { + let a = 496009164746885; + let b = 24764068336973246; + //ab=12283204851556881218686606838710 + + let r = U128{upper: 2516888776885, lower: 8614063320694916486}; + //r=46428403129198069714856710112646 + + // should be + //58711607980754950933543316951356 + //[10881738262824685884, 3182762646142] + let mut res = add_multiply64(r,a,b); + equals_u128(res, 10881738262824685884, 3182762646142) +} + +fn test_add_multiply64_2() -> bool { + let a = 24764068336973246; + let b = 137209507300112; + //ab=3397865615262403032595436803552 + + // r= 1759178078333803271346890843016 + //[16956620749643293576, 95365234715] + let r = U128{upper: 95365234715, lower: 16956620749643293576}; + + // should be + //5157043693596206303942327646568 + //[18148778710141221224, 279563898809] + let mut res = add_multiply64(r,a,b); + + equals_u128(res, 18148778710141221224, 279563898809) +} \ No newline at end of file diff --git a/edwards25519/src/tests/tests_inverse.sw b/edwards25519/src/tests/tests_inverse.sw new file mode 100644 index 0000000..0ddbe89 --- /dev/null +++ b/edwards25519/src/tests/tests_inverse.sw @@ -0,0 +1,40 @@ +library tests_inverse; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; +use std::u128::*; + +pub fn tests_inverse() -> bool { + assert(test_inverse_random()); + true +} + +fn test_inverse_random() -> bool { + /* +4521863078758786565316692829643046466943720546816241094731251542329584501189 +[715325916561861, 1128975921026318, 1696955067652624, 2081297221826529, 175872643896950] + +res +21685099821915697185699787072152716570859124738499628703633803656742289717999 +[2187613694507759, 1614434677729781, 1594711943325299, 378203143193209, 843416921835783] + */ + let a = Element { + l0: 715325916561861, + l1: 1128975921026318, + l2: 1696955067652624, + l3: 2081297221826529, + l4: 175872643896950 + }; + let res: Element = inverse(a); + let res_check = Element { + l0: 2187613694507759, + l1: 1614434677729781, + l2: 1594711943325299, + l3: 378203143193209, + l4: 843416921835783 + }; + // print_el(res); + //res_equals(res, res_check); + true +} \ No newline at end of file diff --git a/edwards25519/src/tests/tests_multiply.sw b/edwards25519/src/tests/tests_multiply.sw new file mode 100644 index 0000000..4ca82a8 --- /dev/null +++ b/edwards25519/src/tests/tests_multiply.sw @@ -0,0 +1,311 @@ +library tests_multiply; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; + +pub fn tests_multiply() -> bool { + // assert(test_multiply_by_0()); + // assert(test_multiply_1_by_1()); + // assert(test_multiply_by_1_small()); + // assert(test_multiply_by_1_large()); + // assert(test_multiply_small_elms()); + // assert(test_multiply_small_elms_2()); + // assert(test_multiply_small_elms_3()); + assert(test_multiply_elms_4()); + // assert(test_multiply_random()); + true +} + +fn test_multiply_by_0() -> bool { + + //a = 2^255 - 21 + let a = Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + let res: Element = multiply(a, ZERO); + res_equals(res, ZERO) +} + +fn test_multiply_1_by_1() -> bool { + let res: Element = multiply(ONE, ONE); + res_equals(res, ONE) +} + +fn test_multiply_by_1_small() -> bool { + + let a = Element{ + l0: 10, + l1: 11, + l2: 12, + l3: 13, + l4: 14 + }; + let res: Element = multiply(a, ONE); + res_equals(res, a) +} + +fn test_multiply_by_1_large() -> bool { + + //a = 2^255 - 21 + let a = Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + let res: Element = multiply(a, ONE); + res_equals(res, a) +} + +fn test_multiply_small_elms() -> bool { + +/* +10 + +11 * 2^51 + +12 * 2^102 + +13 * 2^153 + +14 * 2^204 += +359954121914013970155159550104308900255525248390881418617552906 +*/ + let a = Element{ + l0: 10, + l1: 11, + l2: 12, + l3: 13, + l4: 14 + }; +/* +2 + +3 * 2^51 + +4 * 2^102 + +5 * 2^153 + +6 * 2^204 += +154266052248863123541936069103167134583173917060132914191138818 +*/ + let b = Element{ + l0: 2, + l1: 3, + l2: 4, + l3: 5, + l4: 6 + }; + let res: Element = multiply(a, b); + + // = 5913532002873104218298081466563921672602110506934768320078942312 + // = [4200, 3719, 2909, 1752, 230] + res_equals(res, Element { + l0: 4200, + l1: 3719, + l2: 2909, + l3: 1752, + l4: 230 + }) +} + +fn test_multiply_small_elms_2() -> bool { + +/* +10 + +11 * 2^51 + +12 * 2^102 + +1292655137982008 * 2^153 + +14 * 2^204 += +374713634419208422705277081530098872100895088375962880689831946 +*/ + let a = Element{ + l0: 10, + l1: 11, + l2: 12, + l3: 1292655137982008, + l4: 14 + }; +/* +2 + +3 * 2^51 + +4 * 2^102 + +5 * 2^153 + +6 * 2^204 += +154266052248863123541936069103167134583173917060132914191138818 +*/ + let b = Element{ + l0: 2, + l1: 3, + l2: 4, + l3: 5, + l4: 6 + }; + let res: Element = multiply(a, b); + +//41810357909193713957696946830561922437378062290049323977762016618127807079743 +//[1414398498170175, 1205048169289895, 995697840409273, 333510462280559, 1626165600260968] + res_equals(res, Element { + l0: 1414398498170175, + l1: 1205048169289895, + l2: 995697840409273, + l3: 333510462280559, + l4: 1626165600260968 + }) +} + +fn test_multiply_small_elms_3() -> bool { + +/* +1292655137982008 + +1303372017735434 * 2^51 + +595911506101250 * 2^102 + +601879629470779 * 2^153 + +50591579140481 * 2^204 += +1300760531839662334344262085631565818852980666446405835776058138544728770104 +*/ + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; +/* +2 + +3 * 2^51 + +4 * 2^102 + +5 * 2^153 + +6 * 2^204 += +154266052248863123541936069103167134583173917060132914191138818 +*/ + let b = Element{ + l0: 2, + l1: 3, + l2: 4, + l3: 5, + l4: 6 + }; + let res: Element = multiply(a, b); + +//14111756534184466847966592306453182667710228607558444947423180242606813803383 +//[1954506281775991, 334157138245186, 376444288997219, 169499236944723, 548860478185542] res_equals(res, Element { + res_equals(res, Element { + l0: 1954506281775991, + l1: 334157138245186, + l2: 376444288997219, + l3: 169499236944723, + l4: 548860478185542 + }) +} + +fn test_multiply_elms_4() -> bool { + +/* +1292655137982008 + +1303372017735434 * 2^51 + +595911506101250 * 2^102 + +601879629470779 * 2^153 + +50591579140481 * 2^204 += +1300760531839662334344262085631565818852980666446405835776058138544728770104 +*/ + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; +/* +1360902863141127 + +807899991388824 * 2^51 + +335483569739384 * 2^102 + +293961277766182 * 2^153 + +137209507300112 * 2^204 += +3527794837033309378261417350654351403080646879795459845564282655359926745351 +*/ + let b = Element { + l0: 1360902863141127, + l1: 807899991388824, + l2: 335483569739384, + l3: 293961277766182, + l4: 137209507300112 + }; + let res: Element = multiply(a, b); + +//26419211797770492947925431317169526880133696901606348938375383866077151145114 +//[896638975913114, 1000789340506524, 355992668009873, 806477788321681, 1027544741541094] + res_equals(res, Element { + l0: 896638975913114, + l1: 1000789340506524, + l2: 355992668009873, + l3: 806477788321681, + l4: 1027544741541094 + }) +} + +/* +fn test_multiply_random() -> bool { + /* + a = random({2^251}) = 1300760531839662334344262085631565818852980666446405835776058138544728770104 + = [1292655137982008, 1303372017735434, 595911506101250, 601879629470779, 50591579140481] + = Element { + 1292655137982008, + 1303372017735434, + 595911506101250, + 601879629470779, + 50591579140481 + } + + b = random({2^251}) = 3527794837033309378261417350654351403080646879795459845564282655359926745351 + = [1360902863141127, 807899991388824, 335483569739384, 293961277766182, 137209507300112] + = Element { + 1360902863141127, + 807899991388824, + 335483569739384, + 293961277766182, + 137209507300112 + } + + ab mod p = 26419211797770492947925431317169526880133696901606348938375383866077151145114 + = [896638975913114, 1000789340506524, 355992668009873, 806477788321681, 1027544741541094] + + */ + + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; + + let b = Element { + l0: 1360902863141127, + l1: 807899991388824, + l2: 335483569739384, + l3: 293961277766182, + l4: 137209507300112 + }; + + let ab = Element { + l0: 896638975913114, + l1: 1000789340506524, + l2: 355992668009873, + l3: 806477788321681, + l4: 1027544741541094 + }; + + + let res: Element = multiply(a, b); + // print_el(res); + res_equals(res, ab) +} +*/ \ No newline at end of file diff --git a/edwards25519/src/tests/tests_of_helpers.sw b/edwards25519/src/tests/tests_of_helpers.sw new file mode 100644 index 0000000..365b638 --- /dev/null +++ b/edwards25519/src/tests/tests_of_helpers.sw @@ -0,0 +1,58 @@ +library tests_of_helpers; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; + +pub fn test_helpers() -> bool { + assert(test_get_zero()); + assert(test_equals()); + true +} + +fn test_get_zero() -> bool { + res_equals(ZERO, Element{ l0: 0, l1: 0, l2: 0, l3: 0, l4: 0 }) +} + +fn test_equals() -> bool { + let zero_equals_zero = equals(ZERO, ZERO); + let zero_equals_one = equals(ZERO, ONE); + let ONE_equals_one = equals(ONE, ONE); + + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + let b = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + let c = Element{ + l0: 60, + l1: 5, + l2: 2251799813685247, + l3: 500, + l4: 100 + }; + let a_equals_a = equals(a,a); + let a_equals_b = equals(a,b); //a and b have same coefficients + let a_equals_c = equals(a,c); + let b_equals_c = equals(b,c); + + assert(zero_equals_zero); + assert(!zero_equals_one); + assert(ONE_equals_one); + + assert(a_equals_a); + assert(a_equals_b); + assert(!a_equals_c); + assert(!b_equals_c); + + true +} diff --git a/edwards25519/src/tests/tests_reductions.sw b/edwards25519/src/tests/tests_reductions.sw new file mode 100644 index 0000000..b7fce5b --- /dev/null +++ b/edwards25519/src/tests/tests_reductions.sw @@ -0,0 +1,182 @@ +library tests_reductions; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; + +pub fn test_reductions() -> bool { + assert(test_carry_propagate_1()); + assert(test_carry_propagate_2()); + assert(test_reduce()); + assert(test_reduce_2()); + assert(test_reduce_3()); + assert(test_reduce_4()); + true +} + +fn test_carry_propagate_1() -> bool { + /* + 2^64 -1 = 18446744073709551615 + + 18446744073709551615 + + 18446744073709551615 * 2^51 + + 18446744073709551615 * 2^102 + + 18446744073709551615 * 2^153 + + 18446744073709551615 * 2^204 + */ + let e = Element{ + l0: ~u64::max(), + l1: ~u64::max(), + l2: ~u64::max(), + l3: ~u64::max(), + l4: ~u64::max() }; + + let res = carry_propagate(e); + + // Each limb has a carry of 8191 and a coeff of 2251799813685247 + // So after a round of reduction this is + /* equals + 2251799813685247 + (19*8191) + + (2251799813685247 + 8191) * 2^51 + + (2251799813685247 + 8191) * 2^102 + + (2251799813685247 + 8191) * 2^153 + + (2251799813685247 + 8191) * 2^204 + = 57896044618868696584113898827420068118245036358148060739095062128926159691756 + */ + res_equals(res, Element{ + l0: 2251799813685247 + (19*8191), + l1: 2251799813685247 + 8191, + l2: 2251799813685247 + 8191, + l3: 2251799813685247 + 8191, + l4: 2251799813685247 + 8191 + }) + // Note that this result is >2^255-19 because the function only does 1 round of reduction +} + +fn test_carry_propagate_2() -> bool { + // 2251799813685250 = 2 + 2^51 + let e = Element{ + l0: 2251799813685250, + l1: 0, + l2: 0, + l3: 0, + l4: 0 }; + + let res = carry_propagate(e); + + res_equals(res, Element{ l0: 2, l1: 1, l2: 0, l3: 0, l4: 0 }) +} + +fn test_reduce() -> bool { + // 2251799813685250 = 2 + 2^51 + let e = Element{ + l0: 2251799813685250, + l1: 0, + l2: 0, + l3: 0, + l4: 0 }; + + let res = reduce(e); + + res_equals(res, Element{ l0: 2, l1: 1, l2: 0, l3: 0, l4: 0 }) +} + +fn test_reduce_2() -> bool { + /* + 2^51 + 2 = 2251799813685250 + + 2251799813685250 + + 2251799813685250 * 2^51 + + 2251799813685250 * 2^102 + + 2251799813685250 * 2^153 + + 2251799813685250 * 2^204 + mod 2^255-19 + */ + let e = Element{ + l0: 2251799813685250, + l1: 2251799813685250, + l2: 2251799813685250, + l3: 2251799813685250, + l4: 2251799813685250 }; + + let res = reduce(e); + + /* equals + 21 + + 3 * 2^51 + + 3 * 2^102 + + 3 * 2^153 + + 3 * 2^204 + */ + res_equals(res, Element{ l0: 21, l1: 3, l2: 3, l3: 3, l4: 3 }) +} + +fn test_reduce_3() -> bool { + /* + 2^64 -1 = 18446744073709551615 + + 18446744073709551615 + + 18446744073709551615 * 2^51 + + 18446744073709551615 * 2^102 + + 18446744073709551615 * 2^153 + + 18446744073709551615 * 2^204 + mod 2^255 -19 + = 210598872328406323076114191610044025327778719366270124969594871807 + */ + let e = Element{ + l0: ~u64::max(), + l1: ~u64::max(), + l2: ~u64::max(), + l3: ~u64::max(), + l4: ~u64::max() }; + + let res = reduce(e); + + /* + 155647 + + 8191 * 2^51 + + 8191 * 2^102 + + 8191 * 2^153 + + 8191 * 2^204 + = 210598872328406323076114191610044025327778719366270124969594871807 + */ + res_equals(res, Element{ + l0: 155647, + l1: 8191, + l2: 8191, + l3: 8191, + l4: 8191 + }) +} + +fn test_reduce_4() -> bool { + /* + 4503599627370494 + + 4503599627370494 * 2^51 + + 4503599627370494 * 2^102 + + 4503599627370494 * 2^153 + + 4503599627370494 * 2^204 + mod 2^255 - 19 + + = 36 + */ + let e = Element{ + l0: 4503599627370494, + l1: 4503599627370494, + l2: 4503599627370494, + l3: 4503599627370494, + l4: 4503599627370494 }; + + let res = reduce(e); + + /* + should be 36 + */ + res_equals(res, Element{ + l0: 36, + l1: 0, + l2: 0, + l3: 0, + l4: 0 + }) +} diff --git a/edwards25519/src/tests/tests_rshift.sw b/edwards25519/src/tests/tests_rshift.sw new file mode 100644 index 0000000..4cc6506 --- /dev/null +++ b/edwards25519/src/tests/tests_rshift.sw @@ -0,0 +1,45 @@ +library tests_rshift; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; +use std::u128::*; + +pub fn tests_shift_right_by51() -> bool { + assert(test_shift_right_by51_random()); + assert(test_shift_right_by51_random_2()); + assert(test_shift_right_by51_random_3()); + true +} + +fn test_shift_right_by51_random() -> bool { + + let a = U128{upper: 16, lower:0}; + let res = shift_right_by51(a); + assert(res == 131072); + true +} + +fn test_shift_right_by51_random_2() -> bool { + /* +456464 + (349323232 << 64) = 6443876259705066799772399376 + +>>51 = 2861655916544 + */ + let a = U128{upper: 349323232, lower:456464}; + let res = shift_right_by51(a); + assert(res == 2861655916544); + true +} + +fn test_shift_right_by51_random_3() -> bool { + /* +18446744073709551615 + (349323232 << 64) = 6443876278151810873481494527 + +>>51 = 2861655924735 + */ + let a = U128{upper: 349323232, lower:18446744073709551615}; + let res = shift_right_by51(a); + assert(res == 2861655924735); + true +} diff --git a/edwards25519/src/tests/tests_scalar_mult.sw b/edwards25519/src/tests/tests_scalar_mult.sw new file mode 100644 index 0000000..89aea19 --- /dev/null +++ b/edwards25519/src/tests/tests_scalar_mult.sw @@ -0,0 +1,143 @@ +library tests_scalar_mult; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; + +pub fn tests_scalar_mult() -> bool { + assert(test_mult_by_0()); + assert(test_mult_by_1()); + assert(test_mult_by_2()); + assert(test_mult_by_2_again()); + assert(test_mult_by_large_scalar()); + true +} + +fn test_mult_by_0() -> bool { + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let res: Element = scalar_mult(a, 0); + res_equals(res, ZERO) +} + +fn test_mult_by_1() -> bool { + let a = Element{ + l0: 79611, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 555555333333222, + l4: 2251799813685247 + }; + + let res: Element = scalar_mult(a, 1); + res_equals(res, a) +} + +fn test_mult_by_2() -> bool { + /* + 79611 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 555555333333222 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 79611, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 555555333333222, + l4: 2251799813685247 + }; + + /* 57896044618658058976409158941982667984400415217654165675094330160006532591113 + =[159241, + 2251799813685246, + 2251799813685247, + 1111110666666445, + 2251799813685246] */ + let res: Element = scalar_mult(a, 2); + + res_equals(res, Element{ + l0: 159241, + l1: 2251799813685246, + l2: 2251799813685247, + l3: 1111110666666445, + l4: 2251799813685246 + }) +} + +fn test_mult_by_2_again() -> bool { + // 2^255 - 20 (the largest number) expressed in radix-51 + /* + 2251799813685228 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 2251799813685247 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 2251799813685228, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + + let res: Element = scalar_mult(a, 2); + + /* + 2251799813685227 + 2251799813685247* 2^51 + + 2251799813685247* 2^102 + + 2251799813685247* 2^153 + + 2251799813685247 * 2^204 + */ + res_equals(res, Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }) +} + +fn test_mult_by_large_scalar() -> bool { + /* + 79611 + + 2251799813685247 * 2^51 + + 2251799813685247 * 2^102 + + 555555333333222 * 2^153 + + 2251799813685247 * 2^204 + */ + let a = Element{ + l0: 79611, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 555555333333222, + l4: 2251799813685247 + }; + + /* + 248661618146997193275445770277948644497863737508141907291226725252005565483705230608645 + reduced mod 2^255 - 19 + = 57895961435070841628109209456323569962738033236635300844883400985433276860639 + = [342008245700831, 2251795518717953, 2251799813685247, 536152338865944, 2251796578355658] + */ + // Largest scalar input possible is 2^32 -1 = 4294967295 + let res: Element = scalar_mult(a, 4294967295); + + res_equals(res, Element{ + l0: 342008245700831, + l1: 2251795518717953, + l2: 2251799813685247, + l3: 536152338865944, + l4: 2251796578355658 + }) +} \ No newline at end of file diff --git a/edwards25519/src/tests/tests_square.sw b/edwards25519/src/tests/tests_square.sw new file mode 100644 index 0000000..109e30f --- /dev/null +++ b/edwards25519/src/tests/tests_square.sw @@ -0,0 +1,54 @@ +library tests_square; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; + +pub fn tests_square() -> bool { + // assert(test_square_one()); + // assert(test_square_zero()); + assert(test_square_random()); + true +} + +fn test_square_one() -> bool { + let res: Element = square(ONE); + res_equals(res, ONE) +} + +fn test_square_zero() -> bool { + let res: Element = square(ZERO); + res_equals(res, ZERO) +} + +fn test_square_random() -> bool { + /* + a = 10406084254717298682985401286246103758749877058048555650117556132341049525816 + = [404732633123850, 312158315551803, 595911506101250, 1303372017735434, 1292655137982008] + + a^2 = 108286589516275277577373161473966445398256506358898379115846293425853328039181187715348880647692926795184739504006825065723361671067040986483318450465856 + + a^2 mod p = 31042580993865580304222384966530826020546840941977848835924726765219788840807 + = [1207365348681671, 713663780369466, 912635275234964, 596790797966485, 2144628324130663] + + */ + + let a: Element = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 312158315551803, + l4: 404732633123850 + }; + + let a_square: Element = Element { + l0: 2144628324130663, + l1: 596790797966485, + l2: 912635275234964, + l3: 713663780369466, + l4: 1207365348681671 + }; + + let res: Element = square(a); + res_equals(res, a_square) +} \ No newline at end of file diff --git a/edwards25519/src/tests/tests_sub.sw b/edwards25519/src/tests/tests_sub.sw new file mode 100644 index 0000000..1c867af --- /dev/null +++ b/edwards25519/src/tests/tests_sub.sw @@ -0,0 +1,175 @@ +library tests_sub; + +use ::field_element::*; +use std::assert::assert; +use ::test_helpers::*; + +pub fn tests_substract() -> bool { + assert(test_subtraction_by_0()); + assert(test_subtraction_by_1()); + assert(test_subtraction_by_max()); + assert(test_subtraction_random()); + true +} + +fn test_subtraction_by_0() -> bool { + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let res: Element = subtract(a, ZERO); + res_equals(res, a) +} + +fn test_subtraction_by_1() -> bool { + let a = Element{ + l0: 2251799813685247, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let b = Element{ + l0: 2251799813685246, + l1: 5, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 100 + }; + + let res: Element = subtract(a, ONE); + res_equals(res, b) +} + +fn test_subtraction_by_max() -> bool { + + /* + 2^255 - 21 = 57896044618658097711785492504343953926634992332820282019728792003956564819947 + = [2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685227] + = Element { + 2251799813685227, + 2251799813685247, + 2251799813685247, + 2251799813685247, + 2251799813685247 + } + */ + + let a = Element{ + l0: 2251799813685227, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + /* + 2^255 - 20 = 57896044618658097711785492504343953926634992332820282019728792003956564819948 + = [2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685228] + = Element { + 2251799813685228, + 2251799813685247, + 2251799813685247, + 2251799813685247, + 2251799813685247 + } + */ + + let b = Element { + l0: 2251799813685228, + l1: 2251799813685247, + l2: 2251799813685247, + l3: 2251799813685247, + l4: 2251799813685247 + }; + + let res: Element = subtract(a, b); + let res2: Element = subtract(b,a); + res_equals(res, b) && res_equals(res2, ONE) +} + +fn test_subtraction_random() -> bool { + /* + subtraction of random 2 numbers generated using GP-PARI + + a = random({2^251}) = 1300760531839662334344262085631565818852980666446405835776058138544728770104 + = [50591579140481, 601879629470779, 595911506101250, 1303372017735434, 1292655137982008] + = Element { + 1292655137982008, + 1303372017735434, + 595911506101250, + 601879629470779, + 50591579140481 + } + + b = random({2^251}) = 3527794837033309378261417350654351403080646879795459845564282655359926745351 + = [137209507300112, 293961277766182, 335483569739384, 807899991388824, 1360902863141127] + = Element { + 1360902863141127, + 807899991388824, + 335483569739384, + 293961277766182, + 137209507300112 + } + + b - a = 2227034305193647043917155265022785584227666213349054009788224516815197975247 + = [86617928159630, 1943881461980650, 1991371877323381, 1756327787338638, 68247725159119] + = Element { + 68247725159119, + 1756327787338638, + 1991371877323381, + 1943881461980650, + 86617928159630 + } + a - b + p = 55669010313464450667868337239321168342407326119471228009940567487141366844702 + = [2165181885525617, 307918351704597, 260427936361866, 495472026346609, 2183552088526110] + = Element { + 2183552088526110, + 495472026346609, + 260427936361866, + 307918351704597, + 2165181885525617 + } + */ + + let a = Element { + l0: 1292655137982008, + l1: 1303372017735434, + l2: 595911506101250, + l3: 601879629470779, + l4: 50591579140481 + }; + + let b = Element { + l0: 1360902863141127, + l1: 807899991388824, + l2: 335483569739384, + l3: 293961277766182, + l4: 137209507300112 + }; + + let a_minus_b = Element { + l0: 2183552088526110, + l1: 495472026346609, + l2: 260427936361866, + l3: 307918351704597, + l4: 2165181885525617 + }; + + let b_minus_a = Element { + l0: 68247725159119, + l1: 1756327787338638, + l2: 1991371877323381, + l3: 1943881461980650, + l4: 86617928159630 + }; + + let res: Element = subtract(a, b); + let res2: Element = subtract(b,a); + res_equals(res, a_minus_b) && res_equals(res2, b_minus_a) +} \ No newline at end of file diff --git a/test_contract/.gitignore b/test_contract/.gitignore new file mode 100644 index 0000000..1a6c732 --- /dev/null +++ b/test_contract/.gitignore @@ -0,0 +1,4 @@ +out +target +Cargo.lock +Forc.lock \ No newline at end of file diff --git a/test_contract/Cargo.toml b/test_contract/Cargo.toml new file mode 100644 index 0000000..bccc8fd --- /dev/null +++ b/test_contract/Cargo.toml @@ -0,0 +1,16 @@ +[project] +name = "test_edwards25519" +version = "0.1.0" +authors = ["Hashcloak"] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +fuels = "0.16.1" +fuels-abigen-macro = "0.16.1" +tokio = { version = "1.12", features = ["rt", "macros"] } + +[[test]] +harness = true +name = "integration_tests" +path = "tests/harness.rs" diff --git a/test_contract/Forc.toml b/test_contract/Forc.toml new file mode 100644 index 0000000..cbf0708 --- /dev/null +++ b/test_contract/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Hashcloak"] +entry = "main.sw" +license = "Apache-2.0" +name = "test_edwards25519" + +[dependencies] +edwards25519 = { path = "../edwards25519" } \ No newline at end of file diff --git a/test_contract/src/main.sw b/test_contract/src/main.sw new file mode 100644 index 0000000..35496a2 --- /dev/null +++ b/test_contract/src/main.sw @@ -0,0 +1,82 @@ +contract; +dep test_helpers; + +use edwards25519::*; + +use std::assert::assert; +use std::u128::*; + +use ::test_helpers::*; + +abi EdContract { + #[storage()]fn equals(a: Element, b: Element) -> bool; + #[storage()]fn multiply(a: Element, b: Element) -> Element; + #[storage()]fn square(a: Element) -> Element; + #[storage()]fn subtract(a: Element, b: Element) -> Element; + #[storage()]fn add(a: Element, b: Element) -> Element; + #[storage()]fn carry_propagate(a: Element) -> Element; + #[storage()]fn reduce(a: Element) -> Element; + #[storage()]fn multiply64(a: u64, b: u64) -> U128; + #[storage()]fn add64(a: u64, b: u64, carry: u64) -> (u64, u64); + #[storage()]fn add_multiply64(res: U128, a: u64, b: u64) -> U128; + #[storage()]fn scalar_mult(a: Element, x: u32) -> Element; + #[storage()]fn shift_right_by51(a: U128) -> u64; + + // #[storage()]fn inverse(a: Element) -> Element; + + +} + +impl EdContract for Contract { + #[storage()]fn equals(a: Element, b: Element) -> bool { + equals(a, b) + } + + #[storage()]fn multiply(a: Element, b: Element) -> Element { + multiply(a, b) + } + + #[storage()]fn square(a: Element) -> Element { + square(a) + } + + #[storage()]fn subtract(a: Element, b: Element) -> Element { + subtract(a, b) + } + + #[storage()]fn add(a: Element, b: Element) -> Element { + add(a, b) + } + + #[storage()]fn carry_propagate(a: Element) -> Element { + carry_propagate(a) + } + + #[storage()]fn reduce(a: Element) -> Element { + reduce(a) + } + + #[storage()]fn multiply64(a: u64, b: u64) -> U128 { + multiply64(a, b) + } + + #[storage()]fn add64(a: u64, b: u64, carry: u64) -> (u64, u64) { + add64(a, b, carry) + } + + #[storage()]fn add_multiply64(res: U128, a: u64, b: u64) -> U128 { + add_multiply64(res, a, b) + } + + #[storage()]fn scalar_mult(a: Element, x: u32) -> Element { + scalar_mult(a, x) + } + + #[storage()]fn shift_right_by51(a: U128) -> u64 { + shift_right_by51(a) + } + + // #[storage()]fn inverse(a: Element) -> Element { + // inverse(a) + // } +} \ No newline at end of file diff --git a/test_contract/src/test_helpers.sw b/test_contract/src/test_helpers.sw new file mode 100644 index 0000000..b325e3e --- /dev/null +++ b/test_contract/src/test_helpers.sw @@ -0,0 +1,41 @@ +library test_helpers; + +use edwards25519::*; + +use std::logging::log; +use std::assert::assert; +use std::u128::*; + +pub fn print_el(e: Element) { + log(e.l0); + log(e.l1); + log(e.l2); + log(e.l3); + log(e.l4); +} + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert(res.l0 == should_be.l0); + assert(res.l1 == should_be.l1); + assert(res.l2 == should_be.l2); + assert(res.l3 == should_be.l3); + assert(res.l4 == should_be.l4); + true +} + +pub fn print_U128(a: U128) { + log(a.upper); + log(a.lower); +} + +pub fn equals_U128(res: U128, lower: u64, upper: u64) -> bool { + assert(res.upper == upper); + assert(res.lower == lower); + true +} + +//converts element into array of bytes + +// pub fn bytes_convert (a: Element) -> [u8;32] { +// let a_mod = mod_25519(a); +// } \ No newline at end of file diff --git a/test_contract/tests/add/mod.rs b/test_contract/tests/add/mod.rs new file mode 100644 index 0000000..ca33b7f --- /dev/null +++ b/test_contract/tests/add/mod.rs @@ -0,0 +1,142 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; + +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_add_to_0() { + let a = Element{ + l_0: 8191, + l_1: 225179, + l_2: 155647, + l_3: 81918191, + l_4: 85247 + }; + let expected_res = Element{ + l_0: 8191, + l_1: 225179, + l_2: 155647, + l_3: 81918191, + l_4: 85247 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add(ZERO, a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_add_0() { + let a = Element{ + l_0: 8191, + l_1: 225179, + l_2: 155647, + l_3: 81918191, + l_4: 85247 + }; + let expected_res = Element{ + l_0: 8191, + l_1: 225179, + l_2: 155647, + l_3: 81918191, + l_4: 85247 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add(a, ZERO) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_add_a_to_b() { + let a = Element{ + l_0: 2251799813685247, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let b = Element{ + l_0: 8191, + l_1: 225179, + l_2: 155647, + l_3: 81918191, + l_4: 85247 + }; + let expected_res = Element{ + l_0: 8209, + l_1: 225179, + l_2: 155647, + l_3: 81918191, + l_4: 85247 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_add_a_to_a() { + let a = Element{ + l_0: 2251799813685228, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let a_again = Element{ + l_0: 2251799813685228, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let expected_res = Element{ + l_0: 2251799813685227, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add(a, a_again) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} \ No newline at end of file diff --git a/test_contract/tests/harness.rs b/test_contract/tests/harness.rs new file mode 100644 index 0000000..f98eff0 --- /dev/null +++ b/test_contract/tests/harness.rs @@ -0,0 +1,9 @@ +// mod square; +// mod multiply; +// mod subtract; +// mod add; +// mod reductions; +// mod helpers64; +// mod scalar_mult; +// mod rshift; +mod inverse; \ No newline at end of file diff --git a/test_contract/tests/helpers64/mod.rs b/test_contract/tests/helpers64/mod.rs new file mode 100644 index 0000000..086a957 --- /dev/null +++ b/test_contract/tests/helpers64/mod.rs @@ -0,0 +1,101 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn tests_multiply64() { + let a = 9837491998535547791; + let b = 10009796384580774444; + // let ab = 98471291840283423519614919326553453204; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.multiply64(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res.lower == 5960040633016627860); + assert!(res.upper == 5338139427034470684); +} + +#[tokio::test] +async fn test_add64_random() { + let a = 9837491998535547791; + let b = 10009796384580774444; + let aplusb_carry: (u64, u64) = (1400544309406770619, 1); + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add64(a, b, 0) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res == aplusb_carry); +} + +#[tokio::test] +async fn test_add64_random_with_carry() { + let a = 9837491998535547791; + let b = 10009796384580774444; + let aplusb_carry: (u64, u64) = (1400544309406770620, 1); + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add64(a, b, 1) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res == aplusb_carry); +} + +#[tokio::test] +async fn test_add_multiply64() { + let a = 496009164746885; + let b = 24764068336973246; + let r: U128 = U128{upper: 2516888776885, lower: 8614063320694916486}; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add_multiply64(r, a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res.lower == 10881738262824685884); + assert!(res.upper == 3182762646142); +} + +#[tokio::test] +async fn test_add_multiply64_2() { + let a = 24764068336973246; + let b = 137209507300112; + let r: U128 = U128{upper: 95365234715, lower: 16956620749643293576}; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.add_multiply64(r, a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res.lower == 18148778710141221224); + assert!(res.upper == 279563898809); +} \ No newline at end of file diff --git a/test_contract/tests/inverse/mod.rs b/test_contract/tests/inverse/mod.rs new file mode 100644 index 0000000..91a4576 --- /dev/null +++ b/test_contract/tests/inverse/mod.rs @@ -0,0 +1,56 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; + +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_inverse_random() { + /* + TO_LIMB_T(0xf4df1f341c341746), TO_LIMB_T(0x0a76e6a609d104f1), + TO_LIMB_T(0x8de5476c4c95b6d5), TO_LIMB_T(0x67eb88a9939d83c0), + TO_LIMB_T(0x9a793e85b519952d), TO_LIMB_T(0x11988fe592cae3aa) + */ + let a = Element{ + l_0: 715325916561861, + l_1: 1128975921026318, + l_2: 1696955067652624, + l_3: 2081297221826529, + l_4: 175872643896950 + }; + let expected_res = Element{ + l_0: 2187613694507759, + l_1: 1614434677729781, + l_2: 1594711943325299, + l_3: 378203143193209, + l_4: 843416921835783 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.inverse(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res, expected_res)); +} \ No newline at end of file diff --git a/test_contract/tests/multiply/mod.rs b/test_contract/tests/multiply/mod.rs new file mode 100644 index 0000000..5e2f16e --- /dev/null +++ b/test_contract/tests/multiply/mod.rs @@ -0,0 +1,268 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; + +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_equals() { + let a = Element{ + l_0: 2251799813685247, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + let b = Element{ + l_0: 2251799813685247, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + + let (_instance, _id) = get_contract_instance().await; + + let a_b_equal = _instance.equals(a, b).call().await.unwrap().value; + assert!(a_b_equal); +} + +#[tokio::test] +async fn test_multiply_by_0() { + let (_instance, _id) = get_contract_instance().await; + + //a = 2^255 - 21 + let a = Element{ + l_0: 2251799813685227, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let res = _instance.multiply(a, ZERO) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res, ZERO)); +} + +#[tokio::test] +async fn test_multiply_1_by_1() { + let (_instance, _id) = get_contract_instance().await; + + //a = 2^255 - 21 + let a = Element{ + l_0: 2251799813685227, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let res = _instance.multiply(ONE, ONE) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res, ONE)); +} + +#[tokio::test] +async fn test_multiply_by_1_small() { + let (_instance, _id) = get_contract_instance().await; + + let a = Element{ + l_0: 10, + l_1: 11, + l_2: 12, + l_3: 13, + l_4: 14 + }; + let res = _instance.multiply(a, ONE) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + let expected_res = Element { + l_0: 10, + l_1: 11, + l_2: 12, + l_3: 13, + l_4: 14 + }; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_multiply_by_1_large() { + let (_instance, _id) = get_contract_instance().await; + let a = Element{ + l_0: 2251799813685227, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let res = _instance.multiply(a, ONE) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + let expected_res = Element { + l_0: 2251799813685227, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_multiply_small_elms() { + let (_instance, _id) = get_contract_instance().await; + let a = Element{ + l_0: 10, + l_1: 11, + l_2: 12, + l_3: 13, + l_4: 14 + }; + let b = Element{ + l_0: 2, + l_1: 3, + l_2: 4, + l_3: 5, + l_4: 6 + }; + let res = _instance.multiply(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + let expected_res = Element{ + l_0: 4200, + l_1: 3719, + l_2: 2909, + l_3: 1752, + l_4: 230 + }; + assert!(res_equals(res, expected_res)); +} + + +#[tokio::test] +async fn test_multiply_small_elms_2() { + let (_instance, _id) = get_contract_instance().await; + let a = Element{ + l_0: 10, + l_1: 11, + l_2: 12, + l_3: 1292655137982008, + l_4: 14 + }; + let b = Element{ + l_0: 2, + l_1: 3, + l_2: 4, + l_3: 5, + l_4: 6 + }; + let res = _instance.multiply(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + let expected_res = Element{ + l_0: 1414398498170175, + l_1: 1205048169289895, + l_2: 995697840409273, + l_3: 333510462280559, + l_4: 1626165600260968 + }; + assert!(res_equals(res, expected_res)); +} + + +#[tokio::test] +async fn test_multiply_small_elms_3() { + let (_instance, _id) = get_contract_instance().await; + let a = Element{ + l_0: 1292655137982008, + l_1: 1303372017735434, + l_2: 595911506101250, + l_3: 601879629470779, + l_4: 50591579140481 + }; + let b = Element{ + l_0: 2, + l_1: 3, + l_2: 4, + l_3: 5, + l_4: 6 + }; + let res = _instance.multiply(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + let expected_res = Element{ + l_0: 1954506281775991, + l_1: 334157138245186, + l_2: 376444288997219, + l_3: 169499236944723, + l_4: 548860478185542 + }; + assert!(res_equals(res, expected_res)); +} + + +#[tokio::test] +async fn test_multiply_elms_4() { + let (_instance, _id) = get_contract_instance().await; + let a = Element{ + l_0: 1292655137982008, + l_1: 1303372017735434, + l_2: 595911506101250, + l_3: 601879629470779, + l_4: 50591579140481 + }; + let b = Element{ + l_0: 1360902863141127, + l_1: 807899991388824, + l_2: 335483569739384, + l_3: 293961277766182, + l_4: 137209507300112 + }; + let res = _instance.multiply(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + let expected_res = Element{ + l_0: 896638975913114, + l_1: 1000789340506524, + l_2: 355992668009873, + l_3: 806477788321681, + l_4: 1027544741541094 + }; + assert!(res_equals(res, expected_res)); +} diff --git a/test_contract/tests/reductions/mod.rs b/test_contract/tests/reductions/mod.rs new file mode 100644 index 0000000..b310e0e --- /dev/null +++ b/test_contract/tests/reductions/mod.rs @@ -0,0 +1,182 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; + +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_carry_propagate_1() { + let a = Element{ + l_0: u64::MAX, + l_1: u64::MAX, + l_2: u64::MAX, + l_3: u64::MAX, + l_4: u64::MAX + }; + let expected_res = Element{ + l_0: 2251799813685247 + (19*8191), + l_1: 2251799813685247 + 8191, + l_2: 2251799813685247 + 8191, + l_3: 2251799813685247 + 8191, + l_4: 2251799813685247 + 8191 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.carry_propagate(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_carry_propagate_2() { + let a = Element{ + l_0: 2251799813685250, + l_1: 0, + l_2: 0, + l_3: 0, + l_4: 0 + }; + let expected_res = Element{ + l_0: 2, + l_1: 1, + l_2: 0, + l_3: 0, + l_4: 0 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.carry_propagate(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_reduce() { + let a = Element{ + l_0: 2251799813685250, + l_1: 0, + l_2: 0, + l_3: 0, + l_4: 0 + }; + let expected_res = Element{ + l_0: 2, + l_1: 1, + l_2: 0, + l_3: 0, + l_4: 0 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.reduce(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_reduce_2() { + let a = Element{ + l_0: 2251799813685250, + l_1: 2251799813685250, + l_2: 2251799813685250, + l_3: 2251799813685250, + l_4: 2251799813685250 + }; + let expected_res = Element{ + l_0: 21, + l_1: 3, + l_2: 3, + l_3: 3, + l_4: 3 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.reduce(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_reduce_3() { + let a = Element{ + l_0: u64::MAX, + l_1: u64::MAX, + l_2: u64::MAX, + l_3: u64::MAX, + l_4: u64::MAX + }; + let expected_res = Element{ + l_0: 155647, + l_1: 8191, + l_2: 8191, + l_3: 8191, + l_4: 8191 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.reduce(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_reduce_4() { + let a = Element{ + l_0: 4503599627370494, + l_1: 4503599627370494, + l_2: 4503599627370494, + l_3: 4503599627370494, + l_4: 4503599627370494 + }; + let expected_res = Element{ + l_0: 36, + l_1: 0, + l_2: 0, + l_3: 0, + l_4: 0 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.reduce(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + + diff --git a/test_contract/tests/rshift/mod.rs b/test_contract/tests/rshift/mod.rs new file mode 100644 index 0000000..9c50ff3 --- /dev/null +++ b/test_contract/tests/rshift/mod.rs @@ -0,0 +1,62 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_shift_right_by51_random() { + let a: U128 = U128{upper: 16, lower: 0}; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.shift_right_by51(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res == 131072); +} + +#[tokio::test] +async fn test_shift_right_by51_random_2() { + let a: U128 = U128{upper: 349323232, lower: 456464}; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.shift_right_by51(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res == 2861655916544); +} + +#[tokio::test] +async fn test_shift_right_by51_random_3() { + let a: U128 = U128{upper: 349323232, lower: 18446744073709551615}; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.shift_right_by51(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res == 2861655924735); +} \ No newline at end of file diff --git a/test_contract/tests/scalar_mult/mod.rs b/test_contract/tests/scalar_mult/mod.rs new file mode 100644 index 0000000..c1b87ab --- /dev/null +++ b/test_contract/tests/scalar_mult/mod.rs @@ -0,0 +1,120 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1_000_000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_mult_by_0() { + let a = Element{ + l_0: 2251799813685247, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.scalar_mult(a, 0) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, ZERO)); +} + +#[tokio::test] +async fn test_mult_by_1() { + let a = Element{ + l_0: 79611, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 555555333333222, + l_4: 2251799813685247 + }; + let expected_res = Element{ + l_0: 79611, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 555555333333222, + l_4: 2251799813685247 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.scalar_mult(a, 1) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_mult_by_2() { + let a = Element{ + l_0: 79611, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 555555333333222, + l_4: 2251799813685247 + }; + let expected_res = Element{ + l_0: 159241, + l_1: 2251799813685246, + l_2: 2251799813685247, + l_3: 1111110666666445, + l_4: 2251799813685246 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.scalar_mult(a, 2) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] +async fn test_mult_by_large_scalar() { + let a = Element{ + l_0: 79611, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 555555333333222, + l_4: 2251799813685247 + }; + let expected_res = Element{ + l_0: 342008245700831, + l_1: 2251795518717953, + l_2: 2251799813685247, + l_3: 536152338865944, + l_4: 2251796578355658 + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.scalar_mult(a, 4294967295) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} \ No newline at end of file diff --git a/test_contract/tests/square/mod.rs b/test_contract/tests/square/mod.rs new file mode 100644 index 0000000..777db9c --- /dev/null +++ b/test_contract/tests/square/mod.rs @@ -0,0 +1,47 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; + +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1000000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_square1() { + let a = Element{ + l_0: 1292655137982008, + l_1: 1303372017735434, + l_2: 595911506101250, + l_3: 312158315551803, + l_4: 404732633123850 + }; + let a_square = Element{ + l_0: 2144628324130663, + l_1: 596790797966485, + l_2: 912635275234964, + l_3: 713663780369466, + l_4: 1207365348681671 + }; + + let (_instance, _id) = get_contract_instance().await; + + let squared_res = _instance.square(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(squared_res, a_square)); +} \ No newline at end of file diff --git a/test_contract/tests/subtract/mod.rs b/test_contract/tests/subtract/mod.rs new file mode 100644 index 0000000..8e877e8 --- /dev/null +++ b/test_contract/tests/subtract/mod.rs @@ -0,0 +1,175 @@ +use fuels::{prelude::*, tx::ContractId}; +use fuels_abigen_macro::abigen; + +abigen!(EdContract, "out/debug/test_edwards25519-abi.json"); + +async fn get_contract_instance() -> (EdContract, ContractId) { + let mut wallets = launch_provider_and_get_wallets(WalletsConfig::new_single(Some(1), Some(1000000))).await; + let wallet = wallets.pop().unwrap(); + let id = Contract::deploy("./out/debug/test_edwards25519.bin", &wallet, TxParameters::default()).await.unwrap(); + let instance = EdContract::new(id.to_string(), wallet); + (instance, id) +} + +const ZERO: Element = Element{ l_0: 0, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; +const ONE: Element = Element{ l_0: 1, l_1: 0, l_2: 0, l_3: 0, l_4: 0 }; + +pub fn res_equals(res: Element, should_be: Element) -> bool { + assert!(res.l_0 == should_be.l_0); + assert!(res.l_1 == should_be.l_1); + assert!(res.l_2 == should_be.l_2); + assert!(res.l_3 == should_be.l_3); + assert!(res.l_4 == should_be.l_4); + true +} + +#[tokio::test] +async fn test_subtraction_by_0() { + let a = Element{ + l_0: 2251799813685247, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + let expected_res = Element{ + l_0: 2251799813685247, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + + let (_instance, _id) = get_contract_instance().await; + + let subtract_res = _instance.subtract(a, ZERO) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(subtract_res, expected_res)); +} + +#[tokio::test] +async fn test_subtraction_by_1() { + let a = Element{ + l_0: 2251799813685247, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + let expected_res = Element{ + l_0: 2251799813685246, + l_1: 5, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 100 + }; + + let (_instance, _id) = get_contract_instance().await; + + let subtract_res = _instance.subtract(a, ONE) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(subtract_res, expected_res)); +} + +#[tokio::test] +async fn test_subtraction_by_max() { + let a = Element{ + l_0: 2251799813685227, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let b = Element{ + l_0: 2251799813685228, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let expected_res = Element{ + l_0: 2251799813685228, + l_1: 2251799813685247, + l_2: 2251799813685247, + l_3: 2251799813685247, + l_4: 2251799813685247 + }; + let (_instance, _id) = get_contract_instance().await; + + let subtract_res = _instance.subtract(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(subtract_res, expected_res)); +} + +#[tokio::test] +async fn test_subtraction_random() { + let a = Element{ + l_0: 1292655137982008, + l_1: 1303372017735434, + l_2: 595911506101250, + l_3: 601879629470779, + l_4: 50591579140481 + }; + let b = Element{ + l_0: 1360902863141127, + l_1: 807899991388824, + l_2: 335483569739384, + l_3: 293961277766182, + l_4: 137209507300112 + }; + let expected_res = Element{ + l_0: 2183552088526110, + l_1: 495472026346609, + l_2: 260427936361866, + l_3: 307918351704597, + l_4: 2165181885525617 + }; + let (_instance, _id) = get_contract_instance().await; + + let subtract_res = _instance.subtract(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(subtract_res, expected_res)); +} + +#[tokio::test] +async fn test_subtraction_random2() { + let a = Element{ + l_0: 1292655137982008, + l_1: 1303372017735434, + l_2: 595911506101250, + l_3: 601879629470779, + l_4: 50591579140481 + }; + let b = Element{ + l_0: 1360902863141127, + l_1: 807899991388824, + l_2: 335483569739384, + l_3: 293961277766182, + l_4: 137209507300112 + }; + let expected_res = Element{ + l_0: 68247725159119, + l_1: 1756327787338638, + l_2: 1991371877323381, + l_3: 1943881461980650, + l_4: 86617928159630 + }; + let (_instance, _id) = get_contract_instance().await; + + let subtract_res = _instance.subtract(b, a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(subtract_res, expected_res)); +} \ No newline at end of file diff --git a/tests_bls12_381/.gitignore b/tests_bls12_381/.gitignore new file mode 100644 index 0000000..77d3844 --- /dev/null +++ b/tests_bls12_381/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/tests_bls12_381/Cargo.toml b/tests_bls12_381/Cargo.toml new file mode 100644 index 0000000..7e9e44b --- /dev/null +++ b/tests_bls12_381/Cargo.toml @@ -0,0 +1,15 @@ +[project] +name = "tests_bls12_381" +version = "0.1.0" +authors = ["Hashcloak"] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +fuels = { version = "0.23", features = ["fuel-core-lib"] } +tokio = { version = "1.12", features = ["rt", "macros"] } + +[[test]] +harness = true +name = "integration_tests" +path = "tests/harness.rs" diff --git a/tests_bls12_381/Forc.toml b/tests_bls12_381/Forc.toml new file mode 100644 index 0000000..d092598 --- /dev/null +++ b/tests_bls12_381/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Hashcloak"] +entry = "main.sw" +license = "Apache-2.0" +name = "tests_bls12_381" + +[dependencies] +bls12_381 = { path = "../bls12_381" } diff --git a/tests_bls12_381/src/main.sw b/tests_bls12_381/src/main.sw new file mode 100644 index 0000000..0b1fba8 --- /dev/null +++ b/tests_bls12_381/src/main.sw @@ -0,0 +1,108 @@ +contract; + +use bls12_381::{fp::Fp}; + +// use bls12_381::{ +// fp::Fp, +// fp::from_raw_unchecked, +// fp2::Fp2, +// fp6::Fp6, +// scalar::Scalar}; +use bls12_381::choice::{CtOption, Choice}; + +abi BlsTestContract { + // Works + #[storage(read, write)]fn add_fp(a: Fp, b: Fp) -> Fp; + #[storage(read, write)]fn sub_fp(a: Fp, b: Fp) -> Fp; + // Running this one will give Immediate18TooLarge + // #[storage(read, write)]fn lexicographically_largest_fp(a: Fp) -> Choice; + + // works but takes a long time + #[storage(read, write)]fn mul_fp(a: Fp, b: Fp) -> Fp; + + // works if ran by itself + #[storage(read, write)]fn square_fp(a: Fp) -> Fp; + + // Works + // #[storage(read, write)]fn add_fp2(a: Fp2, b: Fp2) -> Fp2; + // #[storage(read, write)]fn sub_fp2(a: Fp2, b: Fp2) -> Fp2; + // #[storage(read, write)]fn neg_fp2(a: Fp2) -> Fp2; + // #[storage(read, write)]fn lexicographically_largest_fp2(a: Fp2) -> Choice; + + // // not tested, still gives Immediate18TooLarge error + // #[storage(read, write)]fn square_fp2(a: Fp2) -> Fp2; + + // #[storage(read, write)]fn mul_fp2(a: Fp2, b: Fp2) -> Fp2; + + // #[storage(read, write)]fn add_scalar(a: Scalar, b: Scalar) -> Scalar; + +//This function gives an error + // #[storage(read, write)]fn scalar_sqrt(a: Scalar) -> CtOption; + +// These can't be compiled yet.. + // #[storage(read, write)]fn mul_fp6(a: Fp6, b: Fp6) -> Fp6; + // #[storage(read, write)]fn square_fp6(a: Fp6) -> Fp6; + +} + +impl BlsTestContract for Contract { + #[storage(read, write)]fn add_fp(a: Fp, b: Fp) -> Fp { + a + b + } + + #[storage(read, write)]fn sub_fp(a: Fp, b: Fp) -> Fp { + a - b + } +/* + #[storage(read, write)]fn lexicographically_largest_fp(a: Fp) -> Choice { + a.lexicographically_largest() + }*/ + + #[storage(read, write)]fn mul_fp(a: Fp, b: Fp) -> Fp { + a * b + } + + #[storage(read, write)]fn square_fp(a: Fp) -> Fp { + a.square() + } + + // #[storage(read, write)]fn add_fp2(a: Fp2, b: Fp2) -> Fp2 { + // a + b + // } + + // #[storage(read, write)]fn sub_fp2(a: Fp2, b: Fp2) -> Fp2 { + // a - b + // } + + // #[storage(read, write)]fn neg_fp2(a: Fp2) -> Fp2 { + // a.neg() + // } + + // #[storage(read, write)]fn lexicographically_largest_fp2(a: Fp2) -> Choice { + // a.lexicographically_largest() + // } + + // #[storage(read, write)]fn add_scalar(a: Scalar, b: Scalar) -> Scalar { + // a + b + // } + + // #[storage(read, write)]fn square_fp2(a: Fp2) -> Fp2 { + // a.square() + // } + + // #[storage(read, write)]fn mul_fp2(a: Fp2, b: Fp2) -> Fp2 { + // a * b + // } + + // #[storage(read, write)]fn scalar_sqrt(a: Scalar) -> CtOption { + // a.sqrt() + // } + + // #[storage(read, write)]fn mul_fp6(a: Fp6, b: Fp6) -> Fp6 { + // a * b + // } + + // #[storage(read, write)]fn square_fp6(a: Fp6) -> Fp6 { + // a.square() + // } +} \ No newline at end of file diff --git a/tests_bls12_381/tests/harness.rs b/tests_bls12_381/tests/harness.rs new file mode 100644 index 0000000..ed4b844 --- /dev/null +++ b/tests_bls12_381/tests/harness.rs @@ -0,0 +1,4 @@ +mod tests_fp; +// mod tests_fp2; +// mod tests_fp6; +// mod tests_scalar; \ No newline at end of file diff --git a/tests_bls12_381/tests/tests_fp/mod.rs b/tests_bls12_381/tests/tests_fp/mod.rs new file mode 100644 index 0000000..064055e --- /dev/null +++ b/tests_bls12_381/tests/tests_fp/mod.rs @@ -0,0 +1,231 @@ +use fuels::{ + prelude::*, + tx::{ConsensusParameters, ContractId}, +}; + +abigen!(BlsTestContract, "out/debug/tests_bls12_381-abi.json"); + +//TODO move this to a separate helpers file. How to achieve this in Rust/Cargo? +async fn get_contract_instance() -> (BlsTestContract, Bech32ContractId) { + let mut wallet = WalletUnlocked::new_random(None); + let num_assets = 1; + let coins_per_asset = 100; + let amount_per_coin = 100000; + + let (coins, asset_ids) = setup_multiple_assets_coins( + wallet.address(), + num_assets, + coins_per_asset, + amount_per_coin, + ); + + // Custom gas limit + let consensus_parameters_config = ConsensusParameters::DEFAULT.with_max_gas_per_tx(10_000_000_000_000); + + let (client, addr) = setup_test_client(coins, None, Some(consensus_parameters_config)).await; + + let provider = Provider::new(client); + wallet.set_provider(provider.clone()); + + let id = Contract::deploy( + "./out/debug/tests_bls12_381.bin", + &wallet, + TxParameters::default(), + StorageConfiguration::default(), + ).await.unwrap(); + + let instance = BlsTestContractBuilder::new(id.to_string(), wallet).build(); + (instance, id) +} + +pub fn res_equals(res: Fp, should_be: Fp) -> bool { + assert!(res.ls[0] == should_be.ls[0]); + assert!(res.ls[1] == should_be.ls[1]); + assert!(res.ls[2] == should_be.ls[2]); + assert!(res.ls[3] == should_be.ls[3]); + assert!(res.ls[4] == should_be.ls[4]); + assert!(res.ls[5] == should_be.ls[5]); + true +} + +#[tokio::test] //works +async fn test_add_fp() { + let small = Fp{ + ls: [1, 2, 3, 4, 5, 6].to_vec() + }; + let random = Fp{ + ls: [0x3e2528903ca1ef86, 0x270fd67a03bf9e0a, 0xdc70c19599cb699e, 0xebefda8057d5747a, 0xcf20e11f0b1c323, 0xe979cbf960fe51d].to_vec() + }; + let expected_res = Fp{ + ls: [4478030004447473543, 2814704111667093004, 15884408734010272161, 17001047363111187582, 932823543034528552, 1051481384684610851].to_vec() + }; + + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.add_fp(small, random) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res, expected_res)); +} + + +#[tokio::test] //works +async fn test_sub_fp() { + let a = Fp { + ls: [10587454305359941416, 4615625447881587853, 9368308553698906485, 9494054596162055604, 377309137954328098, 766262085408033194].to_vec() + }; + + let b = Fp { + ls: [13403040667047958534, 405585388298286396, 7295341050629342949, 1749456428444609784, 1856600841951774635, 296809876162753174].to_vec() + }; + let expected_res = Fp { + ls: [15631157712021534498, 4210040059583301456, 2072967503069563536, 7744598167717445820, 16967452369712105079, 469452209245280019].to_vec() + }; + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.sub_fp(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, expected_res)); +} + +#[tokio::test] //works, but takes a long time! +async fn test_mul_fp() { + let a = Fp{ ls:[ + 0x0397_a383_2017_0cd4, + 0x734c_1b2c_9e76_1d30, + 0x5ed2_55ad_9a48_beb5, + 0x095a_3c6b_22a7_fcfc, + 0x2294_ce75_d4e2_6a27, + 0x1333_8bd8_7001_1ebb, + ].to_vec()}; + let b = Fp{ ls:[ + 0xb9c3_c7c5_b119_6af7, + 0x2580_e208_6ce3_35c1, + 0xf49a_ed3d_8a57_ef42, + 0x41f2_81e4_9846_e878, + 0xe076_2346_c384_52ce, + 0x0652_e893_26e5_7dc0, + ].to_vec()}; + let c = Fp{ ls:[ + 0xf96e_f3d7_11ab_5355, + 0xe8d4_59ea_00f1_48dd, + 0x53f7_354a_5f00_fa78, + 0x9e34_a4f3_125c_5f83, + 0x3fbe_0c47_ca74_c19e, + 0x01b0_6a8b_bd4a_dfe4, + ].to_vec()}; + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.mul_fp(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert!(res_equals(res, c)); +} + +#[tokio::test] //works, but takes a long time +async fn test_square_fp() { + let a: Fp = Fp { + ls: [0xd215_d276_8e83_191b,//15138237129114720539 + 0x5085_d80f_8fb2_8261,//5802281256283701857 + 0xce9a_032d_df39_3a56,//14887215013780077142 + 0x3e9c_4fff_2ca0_c4bb,//4511568884102382779 + 0x6436_b6f7_f4d9_5dfb,//7221160228616232443 + 0x1060_6628_ad4a_4d90].to_vec()//1180055427263122832 + }; + + let expected_res: Fp = Fp { + ls: [0x33d9_c42a_3cb3_e235, + 0xdad1_1a09_4c4c_d455, + 0xa2f1_44bd_729a_aeba, + 0xd415_0932_be9f_feac, + 0xe27b_c7c4_7d44_ee50, + 0x14b6_a78d_3ec7_a560].to_vec() + }; + + let (_instance, _id) = get_contract_instance().await; + + let res = _instance.square_fp(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res, expected_res)); +} + +/* +Running this one will give Immediate18TooLarge +#[tokio::test] +async fn lexicographically_largest_fp() { + let zero = Fp{ ls: [0,0,0,0,0,0].to_vec()}; + let one = Fp{ ls: [ //=R + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, + ].to_vec()}; + let first = Fp{ ls: [ + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103 + ].to_vec()}; + let second = Fp{ ls: [ + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596, + ].to_vec()}; + let third = Fp{ ls: [ + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206, + ].to_vec()}; + + let (contract_instance, _id) = get_contract_instance().await; + + let res_zero = contract_instance.lexicographically_largest_fp(zero) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + let res_one = contract_instance.lexicographically_largest_fp(one) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + let res_first = contract_instance.lexicographically_largest_fp(first) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + let res_second = contract_instance.lexicographically_largest_fp(second) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + let res_third = contract_instance.lexicographically_largest_fp(third) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + assert!(res_zero.c == 0); + assert!(res_one.c == 0); + assert!(res_first.c == 0); + assert!(res_second.c == 1); + assert!(res_third.c == 1); +} +*/ \ No newline at end of file diff --git a/tests_bls12_381/tests/tests_fp2/mod.rs b/tests_bls12_381/tests/tests_fp2/mod.rs new file mode 100644 index 0000000..857610a --- /dev/null +++ b/tests_bls12_381/tests/tests_fp2/mod.rs @@ -0,0 +1,433 @@ +use fuels::{ + prelude::*, + tx::{ConsensusParameters, ContractId}, +}; + +abigen!(BlsTestContract, "out/debug/tests_bls12_381-abi.json"); + +async fn get_contract_instance() -> (BlsTestContract, Bech32ContractId) { + let mut wallet = WalletUnlocked::new_random(None); + let num_assets = 1; + let coins_per_asset = 100; + let amount_per_coin = 100000; + + let (coins, asset_ids) = setup_multiple_assets_coins( + wallet.address(), + num_assets, + coins_per_asset, + amount_per_coin, + ); + + // Custom gas limit + let consensus_parameters_config = ConsensusParameters::DEFAULT.with_max_gas_per_tx(1000000000); + + let (client, addr) = setup_test_client(coins, None, Some(consensus_parameters_config)).await; + + let provider = Provider::new(client); + wallet.set_provider(provider.clone()); + + let id = Contract::deploy( + "./out/debug/tests_bls12_381.bin", + &wallet, + TxParameters::default(), + StorageConfiguration::default(), + ).await.unwrap(); + + let instance = BlsTestContractBuilder::new(id.to_string(), wallet).build(); + (instance, id) +} + + +pub fn res_equals(res: Fp, should_be: Fp) -> bool { + assert!(res.ls[0] == should_be.ls[0]); + assert!(res.ls[1] == should_be.ls[1]); + assert!(res.ls[2] == should_be.ls[2]); + assert!(res.ls[3] == should_be.ls[3]); + assert!(res.ls[4] == should_be.ls[4]); + assert!(res.ls[5] == should_be.ls[5]); + true +} +/* +#[tokio::test] +async fn test_add_fp2() { + + let a = Fp2 { + c_0: Fp{ls: [ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ].to_vec()}, + c_1: Fp{ls: [ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ].to_vec()}, + }; + let b = Fp2 { + c_0: Fp{ls: [ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ].to_vec()}, + c_1: Fp{ls: [ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ].to_vec()}, + }; + let c = Fp2 { + c_0: Fp{ls: [ + 0x6b82_a9a7_08c1_32d2, + 0x476b_1da3_39ba_5ba4, + 0x848c_0e62_4b91_cd87, + 0x11f9_5955_295a_99ec, + 0xf337_6fce_2255_9f06, + 0x0c3f_e3fa_ce8c_8f43, + ].to_vec()}, + c_1: Fp{ls: [ + 0x6f99_2c12_73ab_5bc5, + 0x3355_1366_17a1_df33, + 0x8b0e_f74c_0aed_aff9, + 0x062f_9246_8ad2_ca12, + 0xe146_9770_738f_d584, + 0x12c3_c3dd_84bc_a26d, + ].to_vec()}, + }; + + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.add_fp2(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res.c_0, c.c_0)); + assert!(res_equals(res.c_1, c.c_1)); +} +*/ +/* +#[tokio::test] +async fn test_sub_fp2() { + + let a = Fp2 { + c_0: Fp{ ls: [ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ].to_vec()}, + c_1: Fp{ ls: [ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ].to_vec()}, + }; + let b = Fp2 { + c_0: Fp{ ls: [ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ].to_vec()}, + c_1: Fp{ ls: [ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ].to_vec()}, + }; + let c = Fp2 { + c_0: Fp{ ls: [ + 0xe1c0_86bb_bf1b_5981, + 0x4faf_c3a9_aa70_5d7e, + 0x2734_b5c1_0bb7_e726, + 0xb2bd_7776_af03_7a3e, + 0x1b89_5fb3_98a8_4164, + 0x1730_4aef_6f11_3cec, + ].to_vec()}, + c_1: Fp{ ls: [ + 0x74c3_1c79_9519_1204, + 0x3271_aa54_79fd_ad2b, + 0xc9b4_7157_4915_a30f, + 0x65e4_0313_ec44_b8be, + 0x7487_b238_5b70_67cb, + 0x0952_3b26_d0ad_19a4, + ].to_vec()}, + }; + + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.sub_fp2(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res.c_0, c.c_0)); + assert!(res_equals(res.c_1, c.c_1)); +} +*/ +/* +#[tokio::test] +async fn test_neg_fp2() { + + let a = Fp2 { + c_0: Fp{ls: [ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ].to_vec()}, + c_1: Fp{ls: [ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ].to_vec()}, + }; + let b = Fp2 { + c_0: Fp{ls: [ + 0xf05c_e7ce_9c11_39d7, + 0x6274_8f57_97e8_a36d, + 0xc4e8_d9df_c664_96df, + 0xb457_88e1_8118_9209, + 0x6949_13d0_8772_930d, + 0x1549_836a_3770_f3cf, + ].to_vec()}, + c_1: Fp{ls: [ + 0x24d0_5bb9_fb9d_491c, + 0xfb1e_a120_c12e_39d0, + 0x7067_879f_c807_c7b1, + 0x60a9_269a_31bb_dab6, + 0x45c2_56bc_fd71_649b, + 0x18f6_9b5d_2b8a_fbde, + ].to_vec()}, + }; + + + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.neg_fp2(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res.c_0, b.c_0)); + assert!(res_equals(res.c_1, b.c_1)); +} +*/ +/* +#[tokio::test] +async fn test_multiplication() { + let a = Fp2 { + c_0: Fp{ ls:[ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ].to_vec()}, + c_1: Fp{ ls:[ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ].to_vec()}, + }; + let b = Fp2 { + c_0: Fp{ ls:[ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ].to_vec()}, + c_1: Fp{ ls:[ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ].to_vec()}, + }; + let c = Fp2 { + c_0: Fp{ ls:[ + 0xf597_483e_27b4_e0f7, + 0x610f_badf_811d_ae5f, + 0x8432_af91_7714_327a, + 0x6a9a_9603_cf88_f09e, + 0xf05a_7bf8_bad0_eb01, + 0x0954_9131_c003_ffae, + ].to_vec()}, + c_1: Fp{ ls:[ + 0x963b_02d0_f93d_37cd, + 0xc95c_e1cd_b30a_73d4, + 0x3087_25fa_3126_f9b8, + 0x56da_3c16_7fab_0d50, + 0x6b50_86b5_f4b6_d6af, + 0x09c3_9f06_2f18_e9f2, + ].to_vec()}, + }; + + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.mul_fp2(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert_eq!(res, c); +} +*/ + + +#[tokio::test] //still gives Immediate18TooLarge :( (12 sept) +async fn test_squaring() { + let a = Fp2 { + c_0: Fp{ ls:[ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ].to_vec()}, + c_1: Fp{ ls:[ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ].to_vec()}, + }; + let b = Fp2 { + c_0: Fp{ ls:[ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ].to_vec()}, + c_1: Fp{ ls:[ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ].to_vec()}, + }; + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.square_fp2(a) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + println!("{}", res.c_0.ls[0]); + println!("{}", res.c_0.ls[1]); + println!("{}", res.c_0.ls[2]); + println!("{}", res.c_0.ls[2]); + println!("{}", res.c_0.ls[4]); + println!("{}", res.c_0.ls[5]); + + println!("{}", res.c_1.ls[0]); + println!("{}", res.c_1.ls[1]); + println!("{}", res.c_1.ls[2]); + println!("{}", res.c_1.ls[2]); + println!("{}", res.c_1.ls[4]); + println!("{}", res.c_1.ls[5]); + + assert_eq!(res.c_0, b.c_0); + assert_eq!(res.c_1, b.c_1); +} + +/* +#[tokio::test]//stripped down version from zkcrypto impl +async fn lexicographically_largest_fp2() { + let zero = Fp2 { + c_0 : Fp{ ls: [0,0,0,0,0,0].to_vec()}, + c_1 : Fp{ ls: [0,0,0,0,0,0].to_vec()}, + }; + let one = Fp2 { + c_0: Fp{ ls: [ //=R + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, + ].to_vec()}, + c_1 : Fp{ ls: [0,0,0,0,0,0].to_vec()} + }; + + let first = Fp2 { + c_0: Fp{ ls: [ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ].to_vec()}, + c_1 : Fp{ ls: [ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0].to_vec()} + }; + let (contract_instance, _id) = get_contract_instance().await; + + let res_zero = contract_instance.lexicographically_largest_fp2(zero) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + let res_one = contract_instance.lexicographically_largest_fp2(one) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + let res_first = contract_instance.lexicographically_largest_fp2(first) + .tx_params(TxParameters::new(None, Some(10_000_000_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(10_000_000_000_000))) + .call().await.unwrap().value; + + assert!(res_zero.c == 0); + assert!(res_one.c == 0); + assert!(res_first.c == 1); +} +*/ \ No newline at end of file diff --git a/tests_bls12_381/tests/tests_fp6/mod.rs b/tests_bls12_381/tests/tests_fp6/mod.rs new file mode 100644 index 0000000..836fc51 --- /dev/null +++ b/tests_bls12_381/tests/tests_fp6/mod.rs @@ -0,0 +1,271 @@ +use fuels::{ + prelude::*, + tx::{ConsensusParameters, ContractId}, +}; + +abigen!(BlsTestContract, "out/debug/tests_bls12_381-abi.json"); + +async fn get_contract_instance() -> (BlsTestContract, Bech32ContractId) { + let mut wallet = WalletUnlocked::new_random(None); + let num_assets = 1; + let coins_per_asset = 100; + let amount_per_coin = 100000; + + let (coins, asset_ids) = setup_multiple_assets_coins( + wallet.address(), + num_assets, + coins_per_asset, + amount_per_coin, + ); + + // Custom gas limit + let consensus_parameters_config = ConsensusParameters::DEFAULT.with_max_gas_per_tx(1000000000); + + let (client, addr) = setup_test_client(coins, None, Some(consensus_parameters_config)).await; + + let provider = Provider::new(client); + wallet.set_provider(provider.clone()); + + let id = Contract::deploy( + "./out/debug/tests_bls12_381.bin", + &wallet, + TxParameters::default(), + StorageConfiguration::default(), + ).await.unwrap(); + + let instance = BlsTestContractBuilder::new(id.to_string(), wallet).build(); + (instance, id) +} + + +fn res_equals_fp(res: Fp, should_be: Fp) -> bool { + assert!(res.ls[0] == should_be.ls[0]); + assert!(res.ls[1] == should_be.ls[1]); + assert!(res.ls[2] == should_be.ls[2]); + assert!(res.ls[3] == should_be.ls[3]); + assert!(res.ls[4] == should_be.ls[4]); + assert!(res.ls[5] == should_be.ls[5]); + true +} + +fn res_equals_fp2(res: Fp2, c: Fp2) -> bool { + assert!(res_equals_fp(res.c_0, c.c_0)); + assert!(res_equals_fp(res.c_1, c.c_1)); + true +} + +fn res_equals(res: Fp6, c: Fp6) -> bool { + assert!(res_equals_fp2(res.c_0, c.c_0)); + assert!(res_equals(res.c_1, c.c_1)); + assert!(res_equals(res.c_2, c.c_2)); + true +} + +fn get_a() -> Fp6 { + let a = Fp6 { + c_0: Fp2 { + c_0: Fp{ls: [ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ].to_vec()}, + c_1: Fp{ls: [ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ].to_vec()}, + }, + c_1: Fp2 { + c_0: Fp{ls: [ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ].to_vec()}, + c_1: Fp{ls: [ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ].to_vec()}, + }, + c_2: Fp2 { + c_0: Fp{ls: [ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ].to_vec()}, + c_1: Fp{ls: [ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ].to_vec()}, + }, + }; + + a +} + +fn get_b() -> Fp6 { + let b = Fp6 { + c_0: Fp2 { + c_0: Fp{ ls: [ + 0xf120_cb98_b16f_d84b, + 0x5fb5_10cf_f3de_1d61, + 0x0f21_a5d0_69d8_c251, + 0xaa1f_d62f_34f2_839a, + 0x5a13_3515_7f89_913f, + 0x14a3_fe32_9643_c247, + ].to_vec()}, + c_1: Fp{ ls: [ + 0x3516_cb98_b16c_82f9, + 0x926d_10c2_e126_1d5f, + 0x1709_e01a_0cc2_5fba, + 0x96c8_c960_b825_3f14, + 0x4927_c234_207e_51a9, + 0x18ae_b158_d542_c44e, + ].to_vec()}, + }, + c_1: Fp2 { + c_0: Fp{ ls: [ + 0xbf0d_cb98_b169_82fc, + 0xa679_10b7_1d1a_1d5c, + 0xb7c1_47c2_b8fb_06ff, + 0x1efa_710d_47d2_e7ce, + 0xed20_a79c_7e27_653c, + 0x02b8_5294_dac1_dfba, + ].to_vec()}, + c_1: Fp{ ls: [ + 0x9d52_cb98_b180_82e5, + 0x621d_1111_5176_1d6f, + 0xe798_8260_3b48_af43, + 0x0ad3_1637_a4f4_da37, + 0xaeac_737c_5ac1_cf2e, + 0x006e_7e73_5b48_b824, + ].to_vec()}, + }, + c_2: Fp2 { + c_0: Fp{ ls: [ + 0xe148_cb98_b17d_2d93, + 0x94d5_1104_3ebe_1d6c, + 0xef80_bca9_de32_4cac, + 0xf77c_0969_2827_95b1, + 0x9dc1_009a_fbb6_8f97, + 0x0479_3199_9a47_ba2b, + ].to_vec()}, + c_1: Fp{ ls: [ + 0x253e_cb98_b179_d841, + 0xc78d_10f7_2c06_1d6a, + 0xf768_f6f3_811b_ea15, + 0xe424_fc9a_ab5a_512b, + 0x8cd5_8db9_9cab_5001, + 0x0883_e4bf_d946_bc32, + ].to_vec()}, + }, + }; + + b +} +/* +#[tokio::test] +async fn test_fp6() { + let (contract_instance, _id) = get_contract_instance().await; + + let a = Fp6 { + c_0: Fp2 { + c_0: Fp{ls: [ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ].to_vec()}, + c_1: Fp{ls: [ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ].to_vec()}, + }, + c_1: Fp2 { + c_0: Fp{ls: [ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ].to_vec()}, + c_1: Fp{ls: [ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ].to_vec()}, + }, + c_2: Fp2 { + c_0: Fp{ls: [ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ].to_vec()}, + c_1: Fp{ls: [ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ].to_vec()}, + }, + }; + + let res = contract_instance.add_fp2(a, b) + .tx_params(TxParameters::new(None, Some(100_000_000), None, None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + + assert!(true); +} */ + +#[tokio::test] +async fn test_square_fp6() { + let (contract_instance, _id) = get_contract_instance().await; + + let res_square = contract_instance.square_fp6(get_a()) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + let res_expected = contract_instance.mul_fp6(get_a(), get_a()) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(res_equals(res_square,res_expected )); +} \ No newline at end of file diff --git a/tests_bls12_381/tests/tests_g1/mod.rs b/tests_bls12_381/tests/tests_g1/mod.rs new file mode 100644 index 0000000..5ae316f --- /dev/null +++ b/tests_bls12_381/tests/tests_g1/mod.rs @@ -0,0 +1,8 @@ + + + +// fn test_projective_negation_and_subtraction() { +// let a = G1Projective::generator().double(); +// assert_eq!(a + (-a), G1Projective::identity()); +// assert_eq!(a + (-a), a - a); +// } \ No newline at end of file diff --git a/tests_bls12_381/tests/tests_scalar/mod.rs b/tests_bls12_381/tests/tests_scalar/mod.rs new file mode 100644 index 0000000..1f33afd --- /dev/null +++ b/tests_bls12_381/tests/tests_scalar/mod.rs @@ -0,0 +1,155 @@ +use fuels::{ + prelude::*, + tx::{ConsensusParameters, ContractId}, +}; + +abigen!(BlsTestContract, "out/debug/tests_bls12_381-abi.json"); + +async fn get_contract_instance() -> (BlsTestContract, Bech32ContractId) { + let mut wallet = WalletUnlocked::new_random(None); + let num_assets = 1; + let coins_per_asset = 100; + let amount_per_coin = 100000; + + let (coins, asset_ids) = setup_multiple_assets_coins( + wallet.address(), + num_assets, + coins_per_asset, + amount_per_coin, + ); + + // Custom gas limit + let consensus_parameters_config = ConsensusParameters::DEFAULT.with_max_gas_per_tx(10_000_000_000_000); + + let (client, addr) = setup_test_client(coins, None, Some(consensus_parameters_config)).await; + + let provider = Provider::new(client); + wallet.set_provider(provider.clone()); + + let id = Contract::deploy( + "./out/debug/tests_bls12_381.bin", + &wallet, + TxParameters::default(), + StorageConfiguration::default(), + ).await.unwrap(); + + let instance = BlsTestContractBuilder::new(id.to_string(), wallet).build(); + (instance, id) +} + +fn scalar_equals(res: Scalar, should_be: Scalar) -> bool { + assert!(res.ls[0] == should_be.ls[0]); + assert!(res.ls[1] == should_be.ls[1]); + assert!(res.ls[2] == should_be.ls[2]); + assert!(res.ls[3] == should_be.ls[3]); + true +} + +#[tokio::test] +async fn test_addition() { + let a: Scalar = Scalar{ ls: [ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ].to_vec()}; + + let a_2 = Scalar{ ls: [ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ].to_vec()}; + + let expected_res = Scalar{ ls:[ + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ].to_vec()}; + + let (contract_instance, _id) = get_contract_instance().await; + + let res = contract_instance.add_scalar(a, a_2) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(scalar_equals(res, expected_res)); + + let a_3 = Scalar{ ls: [ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ].to_vec()}; + + let one = Scalar{ ls: [1,0,0,0].to_vec() }; + let res_2 = contract_instance.add_scalar(a_3, one) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + + assert!(scalar_equals(res_2, Scalar{ ls: [0,0,0,0].to_vec() })); + +} + +/*BLOCKED +error: Internal compiler error: Verification failed: Function anon_11103 return type must match its RET instructions. +Please file an issue on the repository and include the code that triggered this error. + +Eventually, this gave Braqzen the error Immediate18TooLarge + +update sept 12: +error: Internal compiler error: Verification failed: Function one_1 return type must match its RET instructions. +Please file an issue on the repository and include the code that triggered this error. +*/ +#[tokio::test] +async fn test_sqrt() { + let zero = Scalar{ ls: [0,0,0,0].to_vec() }; + let (contract_instance, _id) = get_contract_instance().await; + let square_root = contract_instance.scalar_sqrt(zero) + .tx_params(TxParameters::new(None, Some(100_000_000), None)) + .call_params(CallParameters::new(None, None, Some(100_000_000))) + .call().await.unwrap().value; + assert_eq!(square_root, Scalar{ ls: [0,0,0,0].to_vec() }); +} + + +/*BLOCKED +error: Internal compiler error: Verification failed: Function anon_11103 return type must match its RET instructions. +Please file an issue on the repository and include the code that triggered this error. + +Eventually, this gave Braqzen the error Immediate18TooLarge +*/ +// #[tokio::test] +// async fn test_sqrt() { +// let mut square = Scalar{ ls:[ +// 0x46cd_85a5_f273_077e, +// 0x1d30_c47d_d68f_c735, +// 0x77f6_56f6_0bec_a0eb, +// 0x494a_a01b_df32_468d, +// ].to_vec()}; + +// let mut none_count = 0; +// let (contract_instance, _id) = get_contract_instance().await; + + +// let j = 0; +// while j < 101 { +// let square_root = contract_instance.scalar_sqrt(square) +// .tx_params(TxParameters::new(None, Some(100_000_000), None)) +// .call_params(CallParameters::new(None, None, Some(100_000_000))) +// .call().await.unwrap().value; +// // let square_root = square.sqrt(); +// if square_root.is_none() { +// none_count += 1; +// } else { +// assert_eq!(square_root.unwrap() * square_root.unwrap(), square); +// } +// square -= Scalar::one(); +// j += 1; +// } + +// assert_eq!(49, none_count); +// }