Skip to content

Commit

Permalink
feat: add modexp precomile MVP - only for uint256 numbers (#475)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

ModExp MVP precompile implementation. MVP is missing support for bigint
since there is no bigint or arbitrary size and arithmetics
implementation in cairo 0.10.3. Both integration and unit tests are
passing.


<!-- Give an estimate of the time you spent on this PR in terms of work
days. Did you spend 0.5 days on this PR or rather 2 days? -->

Time spent on this PR: 7 days

## Pull request type

<!-- Please try to limit your pull request to one type, submit multiple
pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or
link to a relevant issue. -->

Resolves #361 

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Calculates modular exponentiation via algorithm that requires
log_2(exponent) loops.
- In cairo this requires ~500k steps for 32-bit numbers
- It's not possible to do this in the hint(if it could get whitelisted)
since checking the result after the hint is too heavy to compute

## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

---------

Co-authored-by: Elias Tazartes <66871571+Eikix@users.noreply.github.com>
Co-authored-by: Dragan Pilipovic <draganpilipovic@Dragans-MacBook-Pro.local>
  • Loading branch information
3 people committed Feb 16, 2023
1 parent ce1004e commit 09c7098
Show file tree
Hide file tree
Showing 7 changed files with 609 additions and 1 deletion.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ markers = [
"EC_RECOVER: Precompile Value 0x01 - Elliptic curve digital signature algorithm (ECDSA) public key recovery function",
"SHA256: Precompile Value 0x02 - Hash function",
"RIPEMD160: Precompile Value 0x03 - Hash function",
"MODEXP: Precompile Value 0x05 - Modular exponentiation MVP - missing support for bigint",
"EC_ADD: Precompile Value 0x06 - Point addition (ADD) on the elliptic curve 'alt_bn128'",
"EC_MUL: Precompile Value 0x07 - Scalar multiplication (MUL) on the elliptic curve 'alt_bn128'",
"BLAKE2F: Precompile Value 0x09 - Blake2 compression function",
Expand Down
64 changes: 64 additions & 0 deletions src/kakarot/precompiles/modexp.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT

%lang starknet

// Starkware dependencies
from starkware.cairo.common.cairo_builtins import HashBuiltin, BitwiseBuiltin
from starkware.cairo.common.uint256 import Uint256
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.memcpy import memcpy

// Internal dependencies
from utils.utils import Helpers
from utils.modexp.modexp_utils import ModExpHelpersUint256

// @title ModExpUint256 MVP Precompile related functions.
// @notice It is an MVP implementation since it only supports uint256 numbers with m_size<=16 and not bigint which requires bigint library in cairo 0.10.
// @author @dragan2234
// @custom:namespace PrecompileModExpUint256
namespace PrecompileModExpUint256 {
const PRECOMPILE_ADDRESS = 0x05;
const MOD_EXP_BYTES_LEN = 32;
// @notice Run the precompile.
// @param input_len The length of input array.
// @param input The input array.
// @return The output length, output array, and gas usage of precompile.
func run{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
range_check_ptr,
bitwise_ptr: BitwiseBuiltin*,
}(_address: felt, input_len: felt, input: felt*) -> (
output_len: felt, output: felt*, gas_used: felt
) {
alloc_locals;

let b_size: Uint256 = Helpers.bytes32_to_uint256(input);
let e_size: Uint256 = Helpers.bytes32_to_uint256(input + MOD_EXP_BYTES_LEN);
let m_size: Uint256 = Helpers.bytes32_to_uint256(input + MOD_EXP_BYTES_LEN * 2);
let b: Uint256 = Helpers.bytes_i_to_uint256(input + MOD_EXP_BYTES_LEN * 3, b_size.low);
let e: Uint256 = Helpers.bytes_i_to_uint256(
input + MOD_EXP_BYTES_LEN * 3 + b_size.low, e_size.low
);
let m: Uint256 = Helpers.bytes_i_to_uint256(
input + MOD_EXP_BYTES_LEN * 3 + b_size.low + e_size.low, m_size.low
);
with_attr error_message("Kakarot: modexp failed") {
let (result) = ModExpHelpersUint256.uint256_mod_exp(b, e, m);
}
let bytes: felt* = alloc();
let (bytes_len_low) = Helpers.felt_to_bytes(result.low, 0, bytes);
let (bytes_len_high) = Helpers.felt_to_bytes(result.high, 0, bytes + bytes_len_low);
local bytes_len: felt;
if (result.high != 0) {
bytes_len = bytes_len_low + bytes_len_high;
} else {
bytes_len = bytes_len_low;
}

let (gas_cost) = ModExpHelpersUint256.calculate_mod_exp_gas(b_size, m_size, e_size, b, e, m);

return (output_len=bytes_len, output=bytes, gas_used=gas_cost);
}
}
3 changes: 2 additions & 1 deletion src/kakarot/precompiles/precompiles.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ from kakarot.precompiles.ecadd import PrecompileEcAdd
from kakarot.precompiles.ecmul import PrecompileEcMul
from kakarot.precompiles.ec_recover import PrecompileEcRecover
from kakarot.precompiles.ripemd160 import PrecompileRIPEMD160
from kakarot.precompiles.modexp import PrecompileModExpUint256
from kakarot.precompiles.sha256 import PrecompileSHA256
from kakarot.stack import Stack

Expand Down Expand Up @@ -121,7 +122,7 @@ namespace Precompiles {
ret;
call PrecompileDataCopy.run; // 0x4
ret;
call not_implemented_precompile; // 0x5
call PrecompileModExpUint256.run; // 0x5
ret;
call PrecompileEcAdd.run; // 0x6
ret;
Expand Down
Loading

0 comments on commit 09c7098

Please sign in to comment.