Skip to content

Commit

Permalink
Merge branch 'main' into feat/levm/ci
Browse files Browse the repository at this point in the history
  • Loading branch information
lima-limon-inc authored Dec 23, 2024
2 parents 7745390 + 65d846b commit ef2290a
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 8 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/vm/levm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ libsecp256k1 = "0.7.1"
sha2 = "0.10.8"
ripemd = "0.1.3"
num-bigint = "0.4.5"
lambdaworks-math = "0.11.0"

[dev-dependencies]
hex = "0.4.3"
Expand Down
4 changes: 4 additions & 0 deletions crates/vm/levm/src/gas_cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,12 @@ pub const IDENTITY_STATIC_COST: u64 = 15;
pub const IDENTITY_DYNAMIC_BASE: u64 = 3;

pub const MODEXP_STATIC_COST: u64 = 200;
pub const MODEXP_DYNAMIC_BASE: u64 = 200;
pub const MODEXP_DYNAMIC_QUOTIENT: u64 = 3;

pub const ECADD_COST: u64 = 150;
pub const ECMUL_COST: u64 = 6000;

pub fn exp(exponent: U256) -> Result<u64, VMError> {
let exponent_byte_size = (exponent
.bits()
Expand Down
147 changes: 142 additions & 5 deletions crates/vm/levm/src/precompiles.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
use bytes::Bytes;
use ethrex_core::{Address, H160, U256};
use keccak_hash::keccak256;
use lambdaworks_math::{
cyclic_group::IsGroup,
elliptic_curve::{
short_weierstrass::curves::bn_254::curve::{BN254Curve, BN254FieldElement},
traits::IsEllipticCurve,
},
traits::ByteConversion,
unsigned_integer::element,
};
use libsecp256k1::{self, Message, RecoveryId, Signature};
use num_bigint::BigUint;
use sha3::Digest;

use crate::{
call_frame::CallFrame,
errors::{InternalError, OutOfGasError, PrecompileError, VMError},
gas_cost::{self, ECRECOVER_COST, MODEXP_STATIC_COST},
gas_cost::{self, ECADD_COST, ECMUL_COST, ECRECOVER_COST, MODEXP_STATIC_COST},
};

pub const ECRECOVER_ADDRESS: H160 = H160([
Expand Down Expand Up @@ -356,12 +365,140 @@ pub fn increase_left_pad(result: &Bytes, m_size: usize) -> Result<Bytes, VMError
}
}

fn ecadd(_calldata: &Bytes, _gas_for_call: u64, _consumed_gas: &mut u64) -> Result<Bytes, VMError> {
Ok(Bytes::new())
pub fn ecadd(
calldata: &Bytes,
gas_for_call: u64,
consumed_gas: &mut u64,
) -> Result<Bytes, VMError> {
// If calldata does not reach the required length, we should fill the rest with zeros
let calldata = fill_with_zeros(calldata, 128)?;

increase_precompile_consumed_gas(gas_for_call, ECADD_COST, consumed_gas)?;

let first_point_x = calldata
.get(0..32)
.ok_or(PrecompileError::ParsingInputError)?;

let first_point_y = calldata
.get(32..64)
.ok_or(PrecompileError::ParsingInputError)?;

let second_point_x = calldata
.get(64..96)
.ok_or(PrecompileError::ParsingInputError)?;

let second_point_y = calldata
.get(96..128)
.ok_or(PrecompileError::ParsingInputError)?;

// If points are zero the precompile should not fail, but the conversion in
// BN254Curve::create_point_from_affine will, so we verify it before the conversion
let first_point_is_zero = U256::from_big_endian(first_point_x).is_zero()
&& U256::from_big_endian(first_point_y).is_zero();
let second_point_is_zero = U256::from_big_endian(second_point_x).is_zero()
&& U256::from_big_endian(second_point_y).is_zero();

let first_point_x = BN254FieldElement::from_bytes_be(first_point_x)
.map_err(|_| PrecompileError::ParsingInputError)?;
let first_point_y = BN254FieldElement::from_bytes_be(first_point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;
let second_point_x = BN254FieldElement::from_bytes_be(second_point_x)
.map_err(|_| PrecompileError::ParsingInputError)?;
let second_point_y = BN254FieldElement::from_bytes_be(second_point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;

if first_point_is_zero && second_point_is_zero {
// If both points are zero, return is zero
Ok(Bytes::from([0u8; 64].to_vec()))
} else if first_point_is_zero {
// If first point is zero, return is second point
let second_point = BN254Curve::create_point_from_affine(second_point_x, second_point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;
let res = [
second_point.x().to_bytes_be(),
second_point.y().to_bytes_be(),
]
.concat();
Ok(Bytes::from(res))
} else if second_point_is_zero {
// If second point is zero, return is first point
let first_point = BN254Curve::create_point_from_affine(first_point_x, first_point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;
let res = [first_point.x().to_bytes_be(), first_point.y().to_bytes_be()].concat();
Ok(Bytes::from(res))
} else {
// If none of the points is zero, return is the sum of both in the EC
let first_point = BN254Curve::create_point_from_affine(first_point_x, first_point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;
let second_point = BN254Curve::create_point_from_affine(second_point_x, second_point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;
let sum = first_point.operate_with(&second_point).to_affine();

if U256::from_big_endian(&sum.x().to_bytes_be()) == U256::zero()
|| U256::from_big_endian(&sum.y().to_bytes_be()) == U256::zero()
{
Ok(Bytes::from([0u8; 64].to_vec()))
} else {
let res = [sum.x().to_bytes_be(), sum.y().to_bytes_be()].concat();
Ok(Bytes::from(res))
}
}
}

fn ecmul(_calldata: &Bytes, _gas_for_call: u64, _consumed_gas: &mut u64) -> Result<Bytes, VMError> {
Ok(Bytes::new())
pub fn ecmul(
calldata: &Bytes,
gas_for_call: u64,
consumed_gas: &mut u64,
) -> Result<Bytes, VMError> {
// If calldata does not reach the required length, we should fill the rest with zeros
let calldata = fill_with_zeros(calldata, 96)?;

increase_precompile_consumed_gas(gas_for_call, ECMUL_COST, consumed_gas)?;

let point_x = calldata
.get(0..32)
.ok_or(PrecompileError::ParsingInputError)?;

let point_y = calldata
.get(32..64)
.ok_or(PrecompileError::ParsingInputError)?;

let scalar = calldata
.get(64..96)
.ok_or(PrecompileError::ParsingInputError)?;
let scalar =
element::U256::from_bytes_be(scalar).map_err(|_| PrecompileError::ParsingInputError)?;

// If point is zero the precompile should not fail, but the conversion in
// BN254Curve::create_point_from_affine will, so we verify it before the conversion
let point_is_zero =
U256::from_big_endian(point_x).is_zero() && U256::from_big_endian(point_y).is_zero();
if point_is_zero {
return Ok(Bytes::from([0u8; 64].to_vec()));
}

let point_x = BN254FieldElement::from_bytes_be(point_x)
.map_err(|_| PrecompileError::ParsingInputError)?;
let point_y = BN254FieldElement::from_bytes_be(point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;

let point = BN254Curve::create_point_from_affine(point_x, point_y)
.map_err(|_| PrecompileError::ParsingInputError)?;

let zero_u256 = element::U256::from(0_u16);
if scalar.eq(&zero_u256) {
Ok(Bytes::from([0u8; 64].to_vec()))
} else {
let mul = point.operate_with_self(scalar).to_affine();
if U256::from_big_endian(&mul.x().to_bytes_be()) == U256::zero()
|| U256::from_big_endian(&mul.y().to_bytes_be()) == U256::zero()
{
Ok(Bytes::from([0u8; 64].to_vec()))
} else {
let res = [mul.x().to_bytes_be(), mul.y().to_bytes_be()].concat();
Ok(Bytes::from(res))
}
}
}

fn ecpairing(
Expand Down
48 changes: 45 additions & 3 deletions crates/vm/levm/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use ethrex_levm::{
db::{cache, CacheDB, Db},
errors::{OutOfGasError, TxResult, VMError},
gas_cost::{
self, ECRECOVER_COST, IDENTITY_DYNAMIC_BASE, IDENTITY_STATIC_COST, MODEXP_STATIC_COST,
RIPEMD_160_DYNAMIC_BASE, RIPEMD_160_STATIC_COST, SHA2_256_DYNAMIC_BASE,
self, ECADD_COST, ECMUL_COST, ECRECOVER_COST, IDENTITY_DYNAMIC_BASE, IDENTITY_STATIC_COST,
MODEXP_STATIC_COST, RIPEMD_160_DYNAMIC_BASE, RIPEMD_160_STATIC_COST, SHA2_256_DYNAMIC_BASE,
SHA2_256_STATIC_COST,
},
memory,
operations::Operation,
precompiles::{ecrecover, identity, modexp, ripemd_160, sha2_256},
precompiles::{ecadd, ecmul, ecrecover, identity, modexp, ripemd_160, sha2_256},
utils::{new_vm_with_ops, new_vm_with_ops_addr_bal_db, new_vm_with_ops_db, ops_to_bytecode},
vm::{word_to_address, Storage, VM},
Environment,
Expand Down Expand Up @@ -4580,3 +4580,45 @@ fn modexp_test_2() {
assert_eq!(result, expected_result);
assert_eq!(consumed_gas, MODEXP_STATIC_COST);
}
#[test]
fn ecadd_test() {
let calldata = hex::decode("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002").unwrap();
let calldata = Bytes::from(calldata);

let mut consumed_gas = 0;
let result = ecadd(&calldata, 10000, &mut consumed_gas).unwrap();

let expected_result = Bytes::from(hex::decode("030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4").unwrap());

assert_eq!(result, expected_result);
assert_eq!(consumed_gas, ECADD_COST);
}

#[test]
fn ecmul_test() {
let calldata = hex::decode("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002").unwrap();
let calldata = Bytes::from(calldata);

let mut consumed_gas = 0;
let result = ecmul(&calldata, 10000, &mut consumed_gas).unwrap();

let expected_result = Bytes::from(hex::decode("030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4").unwrap());

assert_eq!(result, expected_result);
assert_eq!(consumed_gas, ECMUL_COST);
}

#[test]
fn ecmul_test_2() {
// This tests that an infinite in one coordinate implies a zero result
let calldata = hex::decode("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000").unwrap();
let calldata = Bytes::from(calldata);

let mut consumed_gas = 0;
let result = ecmul(&calldata, 10000, &mut consumed_gas).unwrap();

let expected_result = Bytes::from(hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap());

assert_eq!(result, expected_result);
assert_eq!(consumed_gas, ECMUL_COST);
}

0 comments on commit ef2290a

Please sign in to comment.