Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Substraction with borrow #149

Merged
merged 16 commits into from
Oct 2, 2023
Merged

Conversation

fkrause98
Copy link
Contributor

@fkrause98 fkrause98 commented Sep 26, 2023

Changes

How to test

Paste the below snippet into a contract you can call,
my recommendation is to paste it into the Modexp.yul contract
and then run this test: cargo test modexp_tests_98

// Test1: Simple 2 limbs
// First Number:
//  - First Limb: 0x0...0
//  - Second Limb: 0x0...F
// Second Number:
//  - First Limb: 0x0...0
//  - Second Limb: 0x0...A
// [0x0...0, 0x0...F]
// - 
// [0x0...0, 0x0...A]
// ------------------
// [0x0...0, 0x0...5]
// Should print:
// 0 (first limb)
// 5 (second limb)

mstore(0x00, 0x0)
mstore(0x20, 0xF)

mstore(0x40, 0x0)
mstore(0x60, 0xA)

let retStart := 0x80
let _, borrow := bigUintSubstractionWithBorrow(0x00, 0x40, 2, retStart)

let res := mload(retStart)
let res2 := mload(add(retStart, 0x20))
let res3 := mload(add(retStart, 0x40))
let res4 := mload(add(retStart, 0x60))

console_log(res)
console_log(res2)

// Test2: Borrow works as expected
// First Number:
//  - First Limb: 0xFF
//  - Second Limb: 0xAA
// Second Number:
//  - First Limb: 0xAA
//  - Second Limb: 0xFF
// Should print:
// 54 (first limb)
// ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab (second limb)



mstore(0x00, 0xFF)
mstore(0x20, 0xAA)

mstore(0x40, 0xAA)
mstore(0x60, 0xFF)


let retStart := 0x80
let _, borrow := bigUintSubstractionWithBorrow(0x00, 0x40, 2, retStart)

let res := mload(retStart)
let res2 := mload(add(retStart, 0x20))
let res3 := mload(add(retStart, 0x40))
let res4 := mload(add(retStart, 0x60))

console_log(res)
console_log(res2)

// Test3: Big number 2 limb substraction
// I've tested this againts our reference (Lambdaworks) and gives the same result.
// First Number:
mstore(0x20, 0xffffaaaaaaaaaaaaaaaaffffffffffffffffaaaaaaaaaaaaaaaa)
mstore(0x40, 0xffffffffffffffffaaaaaaaaaaaaaaaaffffffffffffffffaaaaaaaaaaaaaaaa)
// Second Number:
mstore(0x60, 0xaaaaffffffffffffffffaaaaaaaaaaaaaaaaffffffffffffffff)
mstore(0x80, 0xaaaaaaaaaaaaaaaaffffffffffffffffaaaaaaaaaaaaaaaaffffffffffffffff)

let retStart := 0x100
let _, borrow := bigUintSubstractionWithBorrow(0x20, 0x60, 2, retStart)

// Should print:
// 5554aaaaaaaaaaaaaaab5555555555555554aaaaaaaaaaaaaaab
// 5555555555555554aaaaaaaaaaaaaaab5555555555555554aaaaaaaaaaaaaaab            

let res := mload(retStart)
let res2 := mload(add(retStart, 0x20))

console_log(res)
console_log(res2)

precompiles/Modexp.yul Outdated Show resolved Hide resolved
precompiles/Modexp.yul Outdated Show resolved Hide resolved
precompiles/Modexp.yul Outdated Show resolved Hide resolved
@jpcenteno
Copy link
Contributor

I wrote these tests to check this PR:

First, paste this snippet to precompiles/Playground.yul:

object "Playground" {
	code { }
	object "Playground_deployed" {
		code {
            function console_log(val) -> {
                let log_address := 0x000000000000000000636F6e736F6c652e6c6f67
                // A big memory address to store the function selector.
                let freeMemPointer := 0x600
                // store the function selector of log(uint256) in memory
                mstore(freeMemPointer, 0xf82c50f1) // mem[0] = 0xf8...
                // store the first argument of log(uint256) in the next memory slot
                mstore(add(freeMemPointer, 0x20), val)
                // call the console.log contract
                if iszero(staticcall(gas(),log_address,add(freeMemPointer, 28),add(freeMemPointer, 0x40),0x00,0x00)) {
                    revert(0,0)
                }
            }

            // 🤌 PASTE EVERY FUNCTION FROM MODEXP HERE 🤌

            // TESTS

            // lhs = [0]
            // rhs = [0]
            // expected = [0]
            let lhsptr := 0x00
            let rhsptr := 0x20
            let resptr := 0x40
            let numberoflimbs := 1
            mstore(lhsptr, 0x00)
            mstore(rhsptr, 0x00)
            bigUintSubtractionWithBorrow(lhsptr, rhsptr, numberoflimbs, resptr)
            console_log(mload(resptr)) // Should print 0
            

            // lhs = [0xFF]
            // rhs = [0]
            // expected = [0xFF]
            let lhsptr := 0x00
            let rhsptr := 0x20
            let resptr := 0x40
            let numberoflimbs := 1
            mstore(lhsptr, 0xFF)
            mstore(rhsptr, 0x00)
            bigUintSubtractionWithBorrow(lhsptr, rhsptr, numberoflimbs, resptr)
            console_log(sub(mload(resptr), 0xFF) // Should print 0

            // lhs = [0xFF]
            // rhs = [0xF0]
            // expected = [0x0F]
            let lhsptr := 0x00
            let rhsptr := 0x20
            let resptr := 0x40
            let numberoflimbs := 1
            mstore(lhsptr, 0xFF)
            mstore(rhsptr, 0xF0)
            bigUintSubtractionWithBorrow(lhsptr, rhsptr, numberoflimbs, resptr)
            console_log(sub(mload(resptr), 0x0F) // Should print 0

            // 2-limb subtraction without borrow
            // lhs      = [0xAB, 0xCD]
            // rhs      = [0xA0, 0x0D]
            // expected = [0x0B, 0xC0]
            let lhsptr := 0x00
            let rhsptr := 0x40
            let resptr := 0x80
            let numberoflimbs := 2
            mstore(add(lhsptr, 0x00), 0xAB)
            mstore(add(lhsptr, 0x20), 0xCD)
            mstore(add(rhsptr, 0x00), 0xA0)
            mstore(add(rhsptr, 0x20), 0x0D)
            bigUintSubtractionWithBorrow(lhsptr, rhsptr, numberoflimbs, resptr)
            console_log(sub(mload(add(resptr, 0x00)), 0x0B) // Should print 0
            console_log(sub(mload(add(resptr, 0x20)), 0xC0) // Should print 0

            // 2-limb subtraction with borrow
            // lhs      = [0x01, 0x00]
            // rhs      = [0x00, 0x01]
            // expected = [0x00, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff]
            let lhsptr := 0x00
            let rhsptr := 0x40
            let resptr := 0x80
            let numberoflimbs := 2
            mstore(add(lhsptr, 0x00), 0x01)
            mstore(add(lhsptr, 0x20), 0x00)
            mstore(add(rhsptr, 0x00), 0x00)
            mstore(add(rhsptr, 0x20), 0x01)
            bigUintSubtractionWithBorrow(lhsptr, rhsptr, numberoflimbs, resptr)
            console_log(sub(mload(add(resptr, 0x00)), 0x0) // should print 0
            console_log(sub(mload(add(resptr, 0x20)), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // should print 0

            // 4-limb subtraction with borrow
            // lhs      = [0xFFFF, 0xAAAA, 0xFFFF, 0xAAAA]
            // rhs      = [0xAAAA, 0xFFFF, 0xAAAA, 0xFFF]
            // expected = [0x5554, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaaab, 0x5555, 0x9aab]
            let lhsptr := 0x00
            let rhsptr := 0x80
            let resptr := 0x100
            let numberoflimbs := 4

            mstore(add(lhsptr, 0x00), 0xFFFF) // lhs[0] = 0xFFFF
            mstore(add(lhsptr, 0x20), 0xAAAA) // lhs[1] = 0xAAAA
            mstore(add(lhsptr, 0x40), 0xFFFF) // lhs[2] = 0xFFFF
            mstore(add(lhsptr, 0x60), 0xAAAA) // lhs[3] = 0xAAAA

            mstore(add(rhsptr, 0x00), 0xAAAA) // rhs[0] = 0xAAAA
            mstore(add(rhsptr, 0x20), 0xFFFF) // rhs[1] = 0xFFFF
            mstore(add(rhsptr, 0x40), 0xAAAA) // rhs[2] = 0xAAAA
            mstore(add(rhsptr, 0x60), 0xFFF)  // rhs[3] = 0xFFF

            bigUintSubtractionWithBorrow(lhsptr, rhsptr, numberoflimbs, resptr)
            console_log(sub(mload(add(resptr, 0x00)), 0x5554) // res[0], should print 0
            console_log(sub(mload(add(resptr, 0x20)), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaaab) // res[1], should print 0
            console_log(sub(mload(add(resptr, 0x40)), 0x5555) // res[2], should print 0
            console_log(sub(mload(add(resptr, 0x60)), 0x9aab) // res[3], should print 0
            

        }
	}
}

Then, copy all your functions from precompiles/ModExp.yul.

after that, compile the contracts and spin up the era-test-node using make run.

When the server is ready, run the tests using

 zksync-era-cli --host localhost --port 8011 call --contract 0x000000000000000000000000000000000000FFFF --function "" --data 00  --private-key 0x850683b40d4a740aa6e745f889a6fdc8327be76e122f5aba645a5b02d0248db8

The expected output should be:

....
Call SUCCESS
=== Console Logs:
0
0
0
0
0
0
0
0
0
0
0
=== Call traces:
....

If you see a non-zero value, it means that one of the resulting limbs does not equal the expected value.

@jpcenteno jpcenteno marked this pull request as ready for review September 28, 2023 20:07
@jpcenteno jpcenteno changed the base branch from main to modexp_reimplementation September 28, 2023 20:39
Copy link
Collaborator

@ilitteri ilitteri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Everything looks good, I suggested a little optimization for the main loop.

precompiles/Modexp.yul Show resolved Hide resolved
@jpcenteno jpcenteno merged commit 2ff02cc into modexp_reimplementation Oct 2, 2023
2 checks passed
@jpcenteno jpcenteno deleted the substraction-with-borrow branch October 2, 2023 13:31
@jpcenteno
Copy link
Contributor

closes #133

jrchatruc added a commit that referenced this pull request Oct 30, 2023
* Initial new implementation

* Fix compilation error

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

* Implement Big UInt Left Shift (#139)

* Add `bigUIntShl` implementation

* Add constants

* Fix compilation

* Implement Big UInt Right Shift (#137)

* Add `bigUIntShr` implementation

* Add constants

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

* Implement bigUIntBitOr

* Fix bigUIntBitOr

* Fix bigUIntBitOr

Co-authored-by: Ivan Litteri <[email protected]>

* Fix missing closing brackets

---------

Co-authored-by: Ivan Litteri <[email protected]>

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

* Implement bigUIntCondSelect

* Fix missing curly braces

Co-authored-by: Ivan Litteri <[email protected]>

* Implement Big UInt Right Shift (#137)

* Add `bigUIntShr` implementation

* Add constants

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

* Implement bigUIntBitOr

* Fix bigUIntBitOr

* Fix bigUIntBitOr

Co-authored-by: Ivan Litteri <[email protected]>

* Fix missing closing brackets

---------

Co-authored-by: Ivan Litteri <[email protected]>

---------

Co-authored-by: Ivan Litteri <[email protected]>

* Implement mul operation for big UInts (#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 (#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 <[email protected]>

* Revert "Update precompiles/Modexp.yul"

This reverts commit 582bc41.

---------

Co-authored-by: Joaquín P. Centeno <[email protected]>
Co-authored-by: Ivan Litteri <[email protected]>

* Refactor `modexp` reimplementation (#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 (#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 <[email protected]>

* Remove `mul` call from `bigUIntInPlaceOrWith1`

Co-authored-by: Ivan Litteri <[email protected]>

* Remove multiplications from copyBigUint

Co-authored-by: Ivan Litteri <[email protected]>

* Optimize bigUIntBitSize loop

Co-authored-by: Ivan Litteri <[email protected]>

* Simplify zeroWithLimbSizeAt

---------

Co-authored-by: Francisco Krause Arnim <[email protected]>
Co-authored-by: IAvecilla <[email protected]>
Co-authored-by: Ivan Litteri <[email protected]>

* Implement mul mod operation for big UInts (#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 <[email protected]>
Co-authored-by: Joaquín P. Centeno <[email protected]>

* 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 (#168)

* Remove redundant parse call data declaration

* Free memory pointer (#169)

* Add free memory pointer function

* Update precompiles/Modexp.yul

Co-authored-by: Ivan Litteri <[email protected]>

* Update precompiles/Modexp.yul

Co-authored-by: Ivan Litteri <[email protected]>

---------

Co-authored-by: Ivan Litteri <[email protected]>

* 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 (#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 <[email protected]>

---------

Co-authored-by: IAvecilla <[email protected]>
Co-authored-by: Ivan Litteri <[email protected]>
Co-authored-by: Francisco Krause Arnim <[email protected]>

* 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 <[email protected]>
Co-authored-by: Joaquín Centeno <[email protected]>
Co-authored-by: Francisco Krause Arnim <[email protected]>
Co-authored-by: Francisco Krause Arnim <[email protected]>
Co-authored-by: IAvecilla <[email protected]>
Co-authored-by: Javier Chatruc <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement Big UInt Subtraction with Borrow for modexp
3 participants