Skip to content

Commit

Permalink
modexp big integers arithmetics (matter-labs#124)
Browse files Browse the repository at this point in the history
* Initial new implementation

* Fix compilation error

* Implementation of add operation for big integers (matter-labs#136)

* Implement Big UInt Left Shift (matter-labs#139)

* Add `bigUIntShl` implementation

* Add constants

* Fix compilation

* Implement Big UInt Right Shift (matter-labs#137)

* Add `bigUIntShr` implementation

* Add constants

* Implement Big UInt Bitwise Or for modexp (matter-labs#135)

* Implement bigUIntBitOr

* Fix bigUIntBitOr

* Fix bigUIntBitOr

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Fix missing closing brackets

---------

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Implement big uint conditional select for modexp (matter-labs#148)

* Implement bigUIntCondSelect

* Fix missing curly braces

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Implement Big UInt Right Shift (matter-labs#137)

* Add `bigUIntShr` implementation

* Add constants

* Implement Big UInt Bitwise Or for modexp (matter-labs#135)

* Implement bigUIntBitOr

* Fix bigUIntBitOr

* Fix bigUIntBitOr

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Fix missing closing brackets

---------

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

---------

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Implement mul operation for big UInts (matter-labs#151)

* First implementation of mul operation for bigints

* Fix multiplication for big integers

* Fix some merge issues

* Improve comments and function docs

* Delete whitespaces

* Substraction with borrow (matter-labs#149)

* First substraction draft

* Fix compile problems

* Working implementation

* Updated code

* Updated code

* Update subtract implementation

* Remove console_log

* Add docs for function

* Update function docs

* Remove tests from from ModExp.yul

* Fix typo

* Restore horrible whitespaces to avoid an ugly merge conflict

* Update precompiles/Modexp.yul

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Revert "Update precompiles/Modexp.yul"

This reverts commit 582bc41a0bb7fb02a1a68717fdf83c6fe432d422.

---------

Co-authored-by: Joaquín P. Centeno <jpcenteno@users.noreply.github.com>
Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Refactor `modexp` reimplementation (matter-labs#156)

* Make Big UInt API functions naming consistent

* Refactor `bigUIntAdd` variable names

* Refactor `bigUIntMul` variable names

* Refactor `subLimbsWithBorrow`

* Refactor `bigUintSubtractionWithBorrow`

* Refactor `bigUIntAdd`

* Fix `bigUIntSubWithBorrow`

* Format `storeLimbValueAtOffset`

* Refactor `bigUIntBitOr`

Made it consistent with the rest of the code convention and naming

* Refactor `bigUIntCondSelect`

Made it consistent with the rest of the code convention and naming

* Reorder `overflowingSubWithBorrow`

* Move comment to modexp API Docs section

* Biguint division (matter-labs#159)

* Division draft

* Non working draft

* Fix compile errors

* Use proper pointers for quotient and remainder

* Add fix note, some more changes

* Add comment

* Implement `big_uint_bit_size`

* Increase pointer to prevent it from steping over console_log

* WIP divrem

* Fix loop and zero initializer

* Push test cases

* Add other test case

* Add docs and tests for `big_uint_inplace_or_1`

* Fix bug related to bit shifting

* Fix borrow return in big uint sub function

* Delete playground file used for debugging

* Fix sub with borrow function

* Add playground again to check more big integer division tests

* Remove playground used for testing

* Write documentation for new shift functions

* Improve naming and documentation for new helper functions

* Rename bigUIntOrWith1 to bigUintInPlaceOrWith1

* Add tmp buffer parameters to bigUIntDivRem. Improve docs.

* Simplify subLimbsWithBorrow

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Remove `mul` call from `bigUIntInPlaceOrWith1`

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Remove multiplications from copyBigUint

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Optimize bigUIntBitSize loop

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Simplify zeroWithLimbSizeAt

---------

Co-authored-by: Francisco Krause Arnim <fkrausear@gmail.com>
Co-authored-by: IAvecilla <iavecilla@fi.uba.ar>
Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Implement mul mod operation for big UInts (matter-labs#161)

* Division draft

* Non working draft

* Fix compile errors

* Use proper pointers for quotient and remainder

* Add fix note, some more changes

* Add comment

* Implement `big_uint_bit_size`

* Increase pointer to prevent it from steping over console_log

* WIP divrem

* Fix loop and zero initializer

* Push test cases

* Add other test case

* Add docs and tests for `big_uint_inplace_or_1`

* Fix bug related to bit shifting

* Fix borrow return in big uint sub function

* Delete playground file used for debugging

* Fix sub with borrow function

* Add playground again to check more big integer division tests

* Remove playground used for testing

* Write documentation for new shift functions

* Improve naming and documentation for new helper functions

* Rename bigUIntOrWith1 to bigUintInPlaceOrWith1

* Add tmp buffer parameters to bigUIntDivRem. Improve docs.

* Add big uint mul mod skeleton

* Remove wrong comment

* Update algorithm comment

* Add limb size doubling and divide by two for mul mod operation

* Functions to duplicate and halve limb size work in place

* Use camelCase

* Remove console_log

* Add docs

* Update doc

---------

Co-authored-by: Francisco Krause Arnim <fkrausear@gmail.com>
Co-authored-by: Joaquín P. Centeno <jpcenteno@users.noreply.github.com>

* Add parseCallData function

* Add function to left-pad big uints

* Remove console log function

* Change left padding functions for big uints to not work in place

* Add `parseCalldata` function (matter-labs#168)

* Remove redundant parse call data declaration

* Free memory pointer (matter-labs#169)

* Add free memory pointer function

* Update precompiles/Modexp.yul

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Update precompiles/Modexp.yul

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

---------

Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>

* Start parsing the input calldata

* Correctly parse call data

* Add left pad steps for modexp inputs

* Add pad if needed function

* Modexp for big UInts skeleton (matter-labs#164)

* WIP: modexp skeleton

* Use of mul mod function for big integers

* imlement aux function to check if big uint is larger than 1

* minor fix

* Restore modexp from target branch

* Fix mul mod

* Finish modexp implementation

* Remove playground used for debugging

* Update modexp with final state of modular exponentiation function

* Fix merge issue

* Change all names to camel case

* fix typo

Co-authored-by: Francisco Krause Arnim <56402156+fkrause98@users.noreply.github.com>

---------

Co-authored-by: IAvecilla <iavecilla@fi.uba.ar>
Co-authored-by: Ivan Litteri <67517699+ilitteri@users.noreply.github.com>
Co-authored-by: Francisco Krause Arnim <56402156+fkrause98@users.noreply.github.com>

* Add simple integration

* Fix calldata buffer in zero check

* Uncomment checks for base cases

* Fix result length to match with mod length

* Fix condition in parse call data

* Update test assertions with new test node updates

* Add comment for tests with a temp patch

* Fix modexp result length

* Fix limb amount for modexp operands

* Clean sratch buffers in each iteration

* Clean sratch buffers for every operation

* Remove unused functions

* Delete free memory pointer usage and calculate pointers manually

* Replace all mul operations for shifts to improve gas usage

* Include basic optimizations

* Add optimizations for reminder calculations

* Add small improvement for main loop in modular exp

* Add temporary fix for modexp test

* Add modex reference script

* Remove unnecesary memory stores

* Reduce iterations in rem function

* Compilation fix

* Print gas used on tests

* Add build script to create gas reports

* Save gas used for each test of the precompiles

* Add aux functions to write lines in each report

* Merge main

* Fix tests lint

* Fix lint in test utils

* Change L1 url

---------

Co-authored-by: Nacho Avecilla <nachoavecilla@gmail.com>
Co-authored-by: Joaquín Centeno <jpcenteno@users.noreply.github.com>
Co-authored-by: Francisco Krause Arnim <56402156+fkrause98@users.noreply.github.com>
Co-authored-by: Francisco Krause Arnim <fkrausear@gmail.com>
Co-authored-by: IAvecilla <iavecilla@fi.uba.ar>
Co-authored-by: Javier Chatruc <jrchatruc@gmail.com>
  • Loading branch information
7 people authored Oct 30, 2023
1 parent bee001d commit a7a5ed4
Show file tree
Hide file tree
Showing 12 changed files with 1,512 additions and 544 deletions.
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
- [Optimizations](ecpairing/optimizations.md)
- [ModExp]()
- [Specification](modexp/spec.md)
- [API Docs](modexp/api.md)
- [Optimizations](modexp/optimizations.md)
27 changes: 27 additions & 0 deletions docs/src/modexp/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# API Docs

## Big Unsigned Integers Arithmetic

### `bigUIntAdd`

### `bigUIntSubWithBorrow`

### `bigUIntMul`

### `bigUIntBitOr`

```
+------------+-----------------------+-------------------------------+-------------------------------+-------------------------------+-----------------+-----------------+--------------------------------------+
| Iteration | currentOffset | lhsCurrentPtr | rhsCurrentPtr | resCurrentPtr | lhsCurrentValue | rhsCurrentValue | resCurrentValue |
+------------+-----------------------+-------------------------------+-------------------------------+-------------------------------+-----------------+-----------------+--------------------------------------+
| 0 | +0x00 | lhsPtr + 0x00 | rhsPtr + 0x00 | resPtr + 0x00 | lhs[0] | rhs[0] | or(lhs[0], rhs[0]) |
| 1 | +0x20 | lhsPtr + 0x20 | rhsPtr + 0x20 | resPtr + 0x20 | lhs[1] | rhs[1] | or(lhs[1], rhs[1]) |
| 2 | +0x40 | lhsPtr + 0x40 | rhsPtr + 0x40 | resPtr + 0x40 | lhs[2] | rhs[2] | or(lhs[2], rhs[2]) |
| | | | | | | | |
| ... | ... | ... | ... | ... | ... | ... | ... |
| | | | | | | | |
| nLimbs - 1 | +(0x20 * (nLimbs - 1) | lhsPtr + (0x20 * (nLimbs - 1) | rhsPtr + (0x20 * (nLimbs - 1) | resPtr + (0x20 * (nLimbs - 1) | lhs[nLimbs - 1] | rhs[nLimbs - 1] | or(lhs[nLimbs - 1], rhs[nLimbs - 1]) |
+------------+-----------------------+-------------------------------+-------------------------------+-------------------------------+-----------------+-----------------+--------------------------------------+
```

### `bigUIntCondSelect`
883 changes: 727 additions & 156 deletions precompiles/Modexp.yul

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions scripts/modexp_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#! /usr/bin/python3
def modular_pow(base, exponent, modulus):
if modulus == 1:
return 0

result = 1
base %= modulus

while exponent > 0:
print(f"Exponent: {hex(exponent)}, result: {hex(result)}, base: {hex(base)}")
if exponent % 2 == 1:
result = (result * base) % modulus
exponent >>= 1
base = (base * base) % modulus

return result

# Example usage:
base = 390298093899999943928098409885853890809480289080848908498808490890809858888590
exponent = 328010176336108753607932954472681594880
modulus = 328083392909999939299399093209090192209
result = modular_pow(base, exponent, modulus)
33 changes: 33 additions & 0 deletions tests/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::fs::OpenOptions;
use std::io::Write;

fn main() {
let directory = "gas_reports";
if !std::path::Path::new(directory).exists() {
std::fs::create_dir(directory).unwrap();
}

let precompiles_report_list: Vec<String> = vec![
"modexp".to_string(),
"ecadd".to_string(),
"ecmul".to_string(),
"ecpairing".to_string(),
"p256verify".to_string(),
"secp256k1verify".to_string(),
];
precompiles_report_list
.into_iter()
.for_each(|mut precompile_name| {
let file_path = format!("{}/{}_report.md", directory, precompile_name);
precompile_name.push_str("_report.md");
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(file_path)
.unwrap();

writeln!(file, "| Test case | Gas used |").unwrap();
writeln!(file, "| --------- | -------- |").unwrap();
});
}
90 changes: 57 additions & 33 deletions tests/tests/ecadd_tests.rs

Large diffs are not rendered by default.

360 changes: 237 additions & 123 deletions tests/tests/ecmul_tests.rs

Large diffs are not rendered by default.

53 changes: 35 additions & 18 deletions tests/tests/ecpairing_tests.rs

Large diffs are not rendered by default.

509 changes: 309 additions & 200 deletions tests/tests/modexp_tests.rs

Large diffs are not rendered by default.

17 changes: 12 additions & 5 deletions tests/tests/p256verify_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use zksync_web3_rs::types::{Address, Bytes, H160};

mod test_utils;
use test_utils::{era_call, parse_call_result};
use test_utils::{era_call, parse_call_result, write_p256verify_gas_result};

pub const P256VERIFTY_PRECOMPILE_ADDRESS: Address = H160([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Expand All @@ -27,7 +26,8 @@ async fn p256verify_valid_signature_one() {
)
.await
.unwrap();
let (era_output, _) = parse_call_result(&era_response);
let (era_output, gas_used) = parse_call_result(&era_response);
write_p256verify_gas_result(gas_used);
assert_eq!(era_output, Bytes::from(RESPONSE_VALID))
}

Expand All @@ -40,7 +40,8 @@ async fn p256verify_valid_signature_two() {
)
.await
.unwrap();
let (era_output, _) = parse_call_result(&era_response);
let (era_output, gas_used) = parse_call_result(&era_response);
write_p256verify_gas_result(gas_used);
assert_eq!(era_output, Bytes::from(RESPONSE_VALID))
}

Expand All @@ -53,7 +54,8 @@ async fn p256verify_invalid_signature() {
)
.await
.unwrap();
let (era_output, _) = parse_call_result(&era_response);
let (era_output, gas_used) = parse_call_result(&era_response);
write_p256verify_gas_result(gas_used);
assert_eq!(era_output, Bytes::from(RESPONSE_INVALID))
}

Expand Down Expand Up @@ -82,6 +84,7 @@ async fn p256verify_invalid_s() {
.err()
.unwrap()
.to_string();

assert_eq!(era_response, EXECUTION_REVERTED)
}

Expand All @@ -96,6 +99,7 @@ async fn p256verify_public_key_inf() {
.err()
.unwrap()
.to_string();

assert_eq!(era_response, EXECUTION_REVERTED)
}

Expand All @@ -110,6 +114,7 @@ async fn p256verify_public_key_x_not_in_field() {
.err()
.unwrap()
.to_string();

assert_eq!(era_response, EXECUTION_REVERTED)
}

Expand All @@ -124,6 +129,7 @@ async fn p256verify_public_key_y_not_in_field() {
.err()
.unwrap()
.to_string();

assert_eq!(era_response, EXECUTION_REVERTED)
}

Expand All @@ -138,5 +144,6 @@ async fn p256verify_public_key_not_in_curve() {
.err()
.unwrap()
.to_string();

assert_eq!(era_response, EXECUTION_REVERTED)
}
17 changes: 9 additions & 8 deletions tests/tests/secp256k1verify_tests.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use zksync_web3_rs::types::{Address, Bytes, H160};

mod test_utils;
use test_utils::{era_call, parse_call_result, write_secp256k1verify_gas_result};

pub const SECP256K1VERIFTY_PRECOMPILE_ADDRESS: Address = H160([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20,
]);

mod test_utils;
use test_utils::era_call;

use crate::test_utils::parse_call_result;

const RESPONSE_VALID: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
];
Expand All @@ -29,7 +27,8 @@ async fn secp256k1verify_valid_signature_one() {
)
.await
.unwrap();
let (era_output, _) = parse_call_result(&era_response);
let (era_output, gas_used) = parse_call_result(&era_response);
write_secp256k1verify_gas_result(gas_used);
assert_eq!(era_output, Bytes::from(RESPONSE_VALID))
}

Expand All @@ -42,7 +41,8 @@ async fn secp256k1verify_valid_signature_two() {
)
.await
.unwrap();
let (era_output, _) = parse_call_result(&era_response);
let (era_output, gas_used) = parse_call_result(&era_response);
write_secp256k1verify_gas_result(gas_used);
assert_eq!(era_output, Bytes::from(RESPONSE_VALID))
}

Expand All @@ -55,7 +55,8 @@ async fn secp256k1verify_invalid_signature() {
)
.await
.unwrap();
let (era_output, _) = parse_call_result(&era_response);
let (era_output, gas_used) = parse_call_result(&era_response);
write_secp256k1verify_gas_result(gas_used);
assert_eq!(era_output, Bytes::from(RESPONSE_INVALID))
}

Expand Down
44 changes: 43 additions & 1 deletion tests/tests/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::env;
use std::{env, fs::OpenOptions, io::Write};
use zksync_web3_rs::{
providers::{Http, Middleware, Provider, ProviderError},
types::{transaction::eip2718::TypedTransaction, Address, Bytes, Eip1559TransactionRequest},
Expand All @@ -21,6 +21,48 @@ pub fn parse_call_result(bytes: &[u8]) -> (Bytes, u32) {
(output.into(), gas_used)
}

fn write_line_to_report(used_gas: u32, report_to_write: &str) {
let mut file = OpenOptions::new()
.append(true)
.open(report_to_write)
.unwrap();

let curr_thread = std::thread::current();
let test_name = curr_thread.name().unwrap();

writeln!(file, "| {test_name} | {used_gas} |").unwrap();
}

#[allow(dead_code)]
pub fn write_modexp_gas_result(used_gas: u32) {
write_line_to_report(used_gas, "gas_reports/modexp_report.md");
}

#[allow(dead_code)]
pub fn write_ecadd_gas_result(used_gas: u32) {
write_line_to_report(used_gas, "gas_reports/ecadd_report.md");
}

#[allow(dead_code)]
pub fn write_ecmul_gas_result(used_gas: u32) {
write_line_to_report(used_gas, "gas_reports/ecmul_report.md");
}

#[allow(dead_code)]
pub fn write_ecpairing_gas_result(used_gas: u32) {
write_line_to_report(used_gas, "gas_reports/ecpairing_report.md");
}

#[allow(dead_code)]
pub fn write_p256verify_gas_result(used_gas: u32) {
write_line_to_report(used_gas, "gas_reports/p256verify_report.md");
}

#[allow(dead_code)]
pub fn write_secp256k1verify_gas_result(used_gas: u32) {
write_line_to_report(used_gas, "gas_reports/secp256k1verify_report.md");
}

pub fn eth_provider() -> Provider<Http> {
let url: String =
env::var("ZKSYNC_WEB3_RS_L1_PROVIDER_URL").unwrap_or(DEFAULT_L1_PROVIDER_URL.to_owned());
Expand Down

0 comments on commit a7a5ed4

Please sign in to comment.