diff --git a/Cargo.lock b/Cargo.lock index 7be75cda91..8a347e4ee4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6509,6 +6509,7 @@ dependencies = [ "petgraph 0.5.1", "primitive-types 0.12.2", "revm-precompile", + "revm-primitives", "rlp", "serde 1.0.202", "serde_bytes", diff --git a/Cargo.toml b/Cargo.toml index aced115c7e..06033c07f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -295,6 +295,7 @@ bitcoincore-rpc-json = "0.18.0" toml = "0.8.12" csv = "1.2.1" revm-precompile = "7.0.0" +revm-primitives = "4.0.0" # Note: the BEGIN and END comments below are required for external tooling. Do not remove. # BEGIN MOVE DEPENDENCIES diff --git a/frameworks/moveos-stdlib/Cargo.toml b/frameworks/moveos-stdlib/Cargo.toml index 3e07fdf42b..9fb722501a 100644 --- a/frameworks/moveos-stdlib/Cargo.toml +++ b/frameworks/moveos-stdlib/Cargo.toml @@ -35,6 +35,7 @@ primitive-types = { workspace = true } bech32 = { workspace = true } bs58 = { workspace = true, features = ["check"] } revm-precompile = { workspace = true } +revm-primitives = { workspace = true } move-binary-format = { workspace = true } move-bytecode-utils = { workspace = true } diff --git a/frameworks/moveos-stdlib/doc/evm.md b/frameworks/moveos-stdlib/doc/evm.md index 0d212693b4..f681d098c7 100644 --- a/frameworks/moveos-stdlib/doc/evm.md +++ b/frameworks/moveos-stdlib/doc/evm.md @@ -6,12 +6,20 @@ - [Constants](#@Constants_0) +- [Function `ec_recover`](#0x2_evm_ec_recover) - [Function `sha2_256`](#0x2_evm_sha2_256) +- [Function `ripemd_160`](#0x2_evm_ripemd_160) +- [Function `identity`](#0x2_evm_identity) +- [Function `modexp`](#0x2_evm_modexp) - [Function `ec_add`](#0x2_evm_ec_add) +- [Function `ec_mul`](#0x2_evm_ec_mul) - [Function `ec_pairing`](#0x2_evm_ec_pairing) +- [Function `blake2f`](#0x2_evm_blake2f) +- [Function `point_evaluation`](#0x2_evm_point_evaluation)
use 0x1::hash;
+use 0x2::hash;
 
@@ -21,6 +29,15 @@ ## Constants + + + + +
const ErrorBlake2fFailed: u64 = 9;
+
+ + + @@ -30,6 +47,15 @@ + + + + +
const ErrorEcMulFailed: u64 = 7;
+
+ + + @@ -39,11 +65,57 @@ - + -
const ErrorInvalidCoordinate: u64 = 11;
+
const ErrorEcRecoverFailed: u64 = 1;
+
+ + + + + + + +
const ErrorInvalidInputSize: u64 = 11;
+
+ + + + + + + +
const ErrorModexpFailed: u64 = 5;
+
+ + + + + + + +
const ErrorPointEvaluationFailed: u64 = 10;
+
+ + + + + +## Function `ec_recover` + +@param hash: Keccack-256 hash of the transaction. +@param v: Recovery identifier, expected to be either 27 or 28. +@param r: x-value, expected to be in the range ]0; secp256k1n[. +@param s: Expected to be in the range ]0; secp256k1n[. + +@return public_address: The recovered 20-byte address right aligned to 32 bytes. + +Elliptic curve digital signature algorithm (ECDSA) public key recovery function. + + +
public fun ec_recover(hash: vector<u8>, v: vector<u8>, r: vector<u8>, s: vector<u8>): vector<u8>
 
@@ -52,7 +124,9 @@ ## Function `sha2_256` -@param data: Arbitrary binary data to hash +@param data: Data to hash with SHA2-256. + +@return hash: The result hash. Hash function. @@ -62,6 +136,59 @@ Hash function. + + +## Function `ripemd_160` + +@param data: Data to hash with RIPEMD-160. + +@return hash: The result 20-byte hash right aligned to 32 bytes. + +Hash function. + + +
public fun ripemd_160(data: vector<u8>): vector<u8>
+
+ + + + + +## Function `identity` + +@param data: Data to return. + +@return data: Data from input. + +Returns the input. + + +
public fun identity(data: vector<u8>): vector<u8>
+
+ + + + + +## Function `modexp` + +@param b_size: Byte size of B. +@param e_size: Byte size of E. +@param m_size: Byte size of M. +@param b: Base as unsigned integer. +@param e: Exponent as unsigned integer, if zero, then B ** E will be one. +@param m: Modulo as unsigned integer, if zero, then returns zero. + +@return value: Result of the computation, with the same number of bytes as M. + +Arbitrary-precision exponentiation under modulo. + + +
public fun modexp(b_size: vector<u8>, e_size: vector<u8>, m_size: vector<u8>, b: vector<u8>, e: vector<u8>, m: vector<u8>): vector<u8>
+
+ + + ## Function `ec_add` @@ -71,9 +198,12 @@ Hash function. @param x2: X coordinate of the second point on the elliptic curve 'alt_bn128'. @param y2: Y coordinate of the second point on the elliptic curve 'alt_bn128'. +@return x: X coordinate of the result point on the elliptic curve 'alt_bn128'. +@return y: Y coordinate of the result point on the elliptic curve 'alt_bn128'. + Notes: The point at infinity is encoded with both field x and y at 0. -Point addition (ADD) on the elliptic curve 'alt_bn128' +Point addition (ADD) on the elliptic curve 'alt_bn128'.
public fun ec_add(x1: vector<u8>, y1: vector<u8>, x2: vector<u8>, y2: vector<u8>): (vector<u8>, vector<u8>)
@@ -81,6 +211,27 @@ Point addition (ADD) on the elliptic curve 'alt_bn128'
 
 
 
+
+
+## Function `ec_mul`
+
+@param x1: X coordinate of the first point on the elliptic curve 'alt_bn128'.
+@param y1: Y coordinate of the first point on the elliptic curve 'alt_bn128'.
+@param s: Scalar to use for the multiplication.
+
+@return x: X coordinate of the result point on the elliptic curve 'alt_bn128'.
+@return y: Y coordinate of the result point on the elliptic curve 'alt_bn128'.
+
+Notes: The point at infinity is encoded with both field x and y at 0.
+
+Scalar multiplication (MUL) on the elliptic curve 'alt_bn128'.
+
+
+
public fun ec_mul(x1: vector<u8>, y1: vector<u8>, s: vector<u8>): (vector<u8>, vector<u8>)
+
+ + + ## Function `ec_pairing` @@ -88,6 +239,8 @@ Point addition (ADD) on the elliptic curve 'alt_bn128' @param data: Coordinates of the points. The input must always be a multiple of 6 32-byte values. 0 inputs is valid and returns 1. +@return success: 1 if the pairing was a success, 0 otherwise. + Notes: The point at infinity is encoded with both field x and y at 0. Bilinear function on groups on the elliptic curve 'alt_bn128'. @@ -95,3 +248,44 @@ Bilinear function on groups on the elliptic curve 'alt_bn128'.
public fun ec_pairing(data: vector<u8>): vector<u8>
 
+ + + + + +## Function `blake2f` + +@param rounds: Number of rounds (big-endian unsigned integer). +@param h: State vector (8 8-byte little-endian unsigned integer). +@param m: Message block vector (16 8-byte little-endian unsigned integer). +@param t: Offset counters (2 8-byte little-endian integer). +@param f: Final block indicator flag (0 or 1). + +@return h: State vector (8 8-byte little-endian unsigned integer). + +Compression function F used in the BLAKE2 cryptographic hashing algorithm. + + +
public fun blake2f(rounds: vector<u8>, h: vector<u8>, m: vector<u8>, t: vector<u8>, f: vector<u8>): vector<u8>
+
+ + + + + +## Function `point_evaluation` + +@param versioned_hash: Reference to a blob in the execution layer. +@param x: x-coordinate at which the blob is being evaluated. +@param y: y-coordinate at which the blob is being evaluated. +@param commitment: Commitment to the blob being evaluated. +@param proof: Proof associated with the commitment. + +@return FIELD_ELEMENTS_PER_BLOB: The number of field elements in the blob. +@return : BLS_MODULUS: The modulus used in the BLS signature scheme. + +Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof. Also verify that the provided commitment matches the provided versioned_hash. + + +
public fun point_evaluation(versioned_hash: vector<u8>, x: vector<u8>, y: vector<u8>, commitment: vector<u8>, proof: vector<u8>): (vector<u8>, vector<u8>)
+
diff --git a/frameworks/moveos-stdlib/sources/evm.move b/frameworks/moveos-stdlib/sources/evm.move index 7048960abb..23ab05066f 100644 --- a/frameworks/moveos-stdlib/sources/evm.move +++ b/frameworks/moveos-stdlib/sources/evm.move @@ -3,40 +3,149 @@ // Evm Precompiled Contracts https://www.evm.codes/precompiled?fork=cancun module moveos_std::evm { + use std::vector; + use moveos_std::hash; + const ErrorEcRecoverFailed: u64 = 1; + const ErrorModexpFailed: u64 = 5; const ErrorEcAddFailed: u64 = 6; + const ErrorEcMulFailed: u64 = 7; const ErrorEcPairingFailed: u64 = 8; + const ErrorBlake2fFailed: u64 = 9; + const ErrorPointEvaluationFailed: u64 = 10; + const ErrorInvalidInputSize: u64 = 11; + + /// @param hash: Keccack-256 hash of the transaction. + /// @param v: Recovery identifier, expected to be either 27 or 28. + /// @param r: x-value, expected to be in the range ]0; secp256k1n[. + /// @param s: Expected to be in the range ]0; secp256k1n[. + /// + /// @return public_address: The recovered 20-byte address right aligned to 32 bytes. + /// + /// Elliptic curve digital signature algorithm (ECDSA) public key recovery function. + public native fun ec_recover(hash: vector, v: vector, r: vector, s: vector): vector; - // The coordinate must be 32 bytes. - const ErrorInvalidCoordinate: u64 = 11; - - /// @param data: Arbitrary binary data to hash + /// @param data: Data to hash with SHA2-256. + /// + /// @return hash: The result hash. /// /// Hash function. public fun sha2_256(data: vector): vector { std::hash::sha2_256(data) } + /// @param data: Data to hash with RIPEMD-160. + /// + /// @return hash: The result 20-byte hash right aligned to 32 bytes. + /// + /// Hash function. + public fun ripemd_160(data: vector): vector { + hash::ripemd160(&data) + } + + /// @param data: Data to return. + /// + /// @return data: Data from input. + /// + /// Returns the input. + public fun identity(data: vector): vector { + let data_clone = data; + data_clone + } + + /// @param b_size: Byte size of B. + /// @param e_size: Byte size of E. + /// @param m_size: Byte size of M. + /// @param b: Base as unsigned integer. + /// @param e: Exponent as unsigned integer, if zero, then B ** E will be one. + /// @param m: Modulo as unsigned integer, if zero, then returns zero. + /// + /// @return value: Result of the computation, with the same number of bytes as M. + /// + /// Arbitrary-precision exponentiation under modulo. + public native fun modexp(b_size: vector, e_size: vector, m_size: vector, b: vector, e: vector, m: vector): vector; + /// @param x1: X coordinate of the first point on the elliptic curve 'alt_bn128'. /// @param y1: Y coordinate of the first point on the elliptic curve 'alt_bn128'. /// @param x2: X coordinate of the second point on the elliptic curve 'alt_bn128'. /// @param y2: Y coordinate of the second point on the elliptic curve 'alt_bn128'. + /// + /// @return x: X coordinate of the result point on the elliptic curve 'alt_bn128'. + /// @return y: Y coordinate of the result point on the elliptic curve 'alt_bn128'. /// /// Notes: The point at infinity is encoded with both field x and y at 0. /// - /// Point addition (ADD) on the elliptic curve 'alt_bn128' + /// Point addition (ADD) on the elliptic curve 'alt_bn128'. public native fun ec_add(x1: vector, y1: vector, x2: vector, y2: vector): (vector, vector); + /// @param x1: X coordinate of the first point on the elliptic curve 'alt_bn128'. + /// @param y1: Y coordinate of the first point on the elliptic curve 'alt_bn128'. + /// @param s: Scalar to use for the multiplication. + /// + /// @return x: X coordinate of the result point on the elliptic curve 'alt_bn128'. + /// @return y: Y coordinate of the result point on the elliptic curve 'alt_bn128'. + /// + /// Notes: The point at infinity is encoded with both field x and y at 0. + /// + /// Scalar multiplication (MUL) on the elliptic curve 'alt_bn128'. + public native fun ec_mul(x1: vector, y1: vector, s: vector): (vector, vector); + /// @param data: Coordinates of the points. /// The input must always be a multiple of 6 32-byte values. 0 inputs is valid and returns 1. + /// + /// @return success: 1 if the pairing was a success, 0 otherwise. /// /// Notes: The point at infinity is encoded with both field x and y at 0. /// /// Bilinear function on groups on the elliptic curve 'alt_bn128'. public native fun ec_pairing(data: vector): vector; - #[test_only] - use std::vector; + /// @param rounds: Number of rounds (big-endian unsigned integer). + /// @param h: State vector (8 8-byte little-endian unsigned integer). + /// @param m: Message block vector (16 8-byte little-endian unsigned integer). + /// @param t: Offset counters (2 8-byte little-endian integer). + /// @param f: Final block indicator flag (0 or 1). + /// + /// @return h: State vector (8 8-byte little-endian unsigned integer). + /// + /// Compression function F used in the BLAKE2 cryptographic hashing algorithm. + public native fun blake2f(rounds: vector, h: vector, m: vector, t: vector, f: vector): vector; + + /// @param versioned_hash: Reference to a blob in the execution layer. + /// @param x: x-coordinate at which the blob is being evaluated. + /// @param y: y-coordinate at which the blob is being evaluated. + /// @param commitment: Commitment to the blob being evaluated. + /// @param proof: Proof associated with the commitment. + /// + /// @return FIELD_ELEMENTS_PER_BLOB: The number of field elements in the blob. + /// @return : BLS_MODULUS: The modulus used in the BLS signature scheme. + /// + /// Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof. Also verify that the provided commitment matches the provided versioned_hash. + public native fun point_evaluation(versioned_hash: vector, x: vector, y: vector, commitment: vector, proof: vector): (vector, vector); + + #[test] + fun test_ec_recover() { + let hash = x"456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3"; + let v = x"000000000000000000000000000000000000000000000000000000000000001c"; + let r = x"9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608"; + let s = x"4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada"; + + let public_address = ec_recover(hash, v, r, s); + assert!(public_address == x"0000000000000000000000007156526fbd7a3c72969b54f64e42c10fbb768c8a", ErrorEcRecoverFailed); + } + + #[test] + fun test_modexp() { + let b_size = x"0000000000000000000000000000000000000000000000000000000000000001"; + let e_size = x"0000000000000000000000000000000000000000000000000000000000000001"; + let m_size = x"0000000000000000000000000000000000000000000000000000000000000001"; + let b = x"08"; + let e = x"09"; + let m = x"0a"; + + let value = modexp(b_size, e_size, m_size, b, e, m); + assert!(value == x"08", ErrorModexpFailed); + } #[test] fun test_ec_add() { @@ -50,6 +159,17 @@ module moveos_std::evm { assert!(y3 == x"15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", ErrorEcAddFailed); } + #[test] + fun test_ec_mul() { + let x1 = x"0000000000000000000000000000000000000000000000000000000000000001"; + let y1 = x"0000000000000000000000000000000000000000000000000000000000000002"; + let s = x"0000000000000000000000000000000000000000000000000000000000000002"; + + let (x, y) = ec_mul(x1, y1, s); + assert!(x == x"030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3", ErrorEcMulFailed); + assert!(y == x"15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", ErrorEcMulFailed); + } + #[test] fun test_ec_pairing() { let x1 = x"2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da"; @@ -82,7 +202,7 @@ module moveos_std::evm { } #[test] - #[expected_failure(abort_code = ErrorInvalidCoordinate, location = Self)] + #[expected_failure(abort_code = ErrorInvalidInputSize, location = Self)] fun test_ec_add_input_not_match() { let x1 = x"01"; let y1 = x"02"; @@ -101,4 +221,29 @@ module moveos_std::evm { ec_pairing(x1); } + + #[test] + fun test_blake2f() { + let rounds = x"0000000c"; + let h = x"48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"; + let m = x"6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + let t = x"03000000000000000000000000000000"; + let f = x"01"; + + let h = blake2f(rounds, h, m, t, f); + assert!(h == x"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", ErrorBlake2fFailed); + } + + #[test] + fun test_point_evaluation() { + let versioned_hash = x"01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b"; + let x = x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000"; + let y = x"1522a4a7f34e1ea350ae07c29c96c7e79655aa926122e95fe69fcbd932ca49e9"; + let commitment = x"8f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7"; + let proof = x"a62ad71d14c5719385c0686f1871430475bf3a00f0aa3f7b8dd99a9abc2160744faf0070725e00b60ad9a026a15b1a8c"; + + let (field_elements_per_blob, bls_modulus) = point_evaluation(versioned_hash, x, y, commitment, proof); + assert!(field_elements_per_blob == x"0000000000000000000000000000000000000000000000000000000000001000", ErrorPointEvaluationFailed); + assert!(bls_modulus == x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", ErrorPointEvaluationFailed); + } } \ No newline at end of file diff --git a/frameworks/moveos-stdlib/src/natives/moveos_stdlib/evm.rs b/frameworks/moveos-stdlib/src/natives/moveos_stdlib/evm.rs index 2b838d7c42..f4e2dd4d37 100644 --- a/frameworks/moveos-stdlib/src/natives/moveos_stdlib/evm.rs +++ b/frameworks/moveos-stdlib/src/natives/moveos_stdlib/evm.rs @@ -8,20 +8,108 @@ use move_vm_runtime::native_functions::{NativeContext, NativeFunction}; use move_vm_types::{ loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value, }; -use revm_precompile::bn128; +use revm_precompile::{blake2, bn128, kzg_point_evaluation, modexp, secp256k1}; +use revm_primitives::Env; use smallvec::smallvec; use std::collections::VecDeque; +pub const E_EC_RECOVER_FAILED: u64 = 1; +pub const E_MODEXP_FAILED: u64 = 5; pub const E_EC_ADD_FAILED: u64 = 6; +pub const E_EC_MUL_FAILED: u64 = 7; pub const E_EC_PAIRING_FAILED: u64 = 8; -pub const E_INVALID_COORDINATE: u64 = 11; +pub const E_BLAKE2F_FAILED: u64 = 9; +pub const E_POINT_EVALUATION_FAILED: u64 = 10; +pub const E_INVALID_INPUT_SIZE: u64 = 11; /*************************************************************************************************** - * native fun ec_add - * Implementation of the Move native function `ec_add(x1: vector, y1: vector, x2: vector, y2: vector): (vector, vector)` - * gas cost: ec_add_cost_base | base cost for function call and fixed opers - * + ec_add_data_cost_per_byte * 128 | cost depends on length of message - * + ec_add_data_cost_per_block * num_blocks | cost depends on number of blocks in message + * native function `ec_recover(hash: vector, v: vector, r: vector, s: vector): vector` + **************************************************************************************************/ +pub fn native_ec_recover( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 4); + + let s = pop_arg!(args, Vec); + let r = pop_arg!(args, Vec); + let v = pop_arg!(args, Vec); + let hash = pop_arg!(args, Vec); + + if s.len() != 32 || r.len() != 32 || v.len() != 32 || hash.len() != 32 { + return Ok(NativeResult::err(0.into(), E_INVALID_INPUT_SIZE)); + } + let cost = gas_params.base + (gas_params.per_byte * NumBytes::new(128_u64)); + + let mut data: Vec = Vec::new(); + data.extend(hash); + data.extend(v); + data.extend(r); + data.extend(s); + + match secp256k1::ec_recover_run(&data.into(), 5000) { + Ok((_, out)) => Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(out.to_vec())], + )), + + Err(_) => Ok(NativeResult::err(cost, E_EC_RECOVER_FAILED)), + } +} + +/*************************************************************************************************** + * native function `modexp(b_size: vector, e_size: vector, m_size: vector, b: vector, e: vector, m: vector): vector` + **************************************************************************************************/ +pub fn native_modexp( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 6); + + let m = pop_arg!(args, Vec); + let e = pop_arg!(args, Vec); + let b = pop_arg!(args, Vec); + let m_size = pop_arg!(args, Vec); + let e_size = pop_arg!(args, Vec); + let b_size = pop_arg!(args, Vec); + + let cost = gas_params.base + + (gas_params.per_byte + * NumBytes::new( + m.len() as u64 + + e.len() as u64 + + b.len() as u64 + + m_size.len() as u64 + + e_size.len() as u64 + + b_size.len() as u64, + )); + + let mut data: Vec = Vec::new(); + data.extend(b_size); + data.extend(e_size); + data.extend(m_size); + data.extend(b); + data.extend(e); + data.extend(m); + + match modexp::byzantium_run(&data.into(), 100_000_000_000) { + Ok((_, output)) => Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(output.to_vec())], + )), + + Err(_) => Ok(NativeResult::err(cost, E_MODEXP_FAILED)), + } +} + +/*************************************************************************************************** + * native function `ec_add(x1: vector, y1: vector, x2: vector, y2: vector): (vector, vector)` **************************************************************************************************/ pub fn native_ec_add( gas_params: &FromBytesGasParameters, @@ -38,7 +126,7 @@ pub fn native_ec_add( let x1 = pop_arg!(args, Vec); if y2.len() != 32 || x2.len() != 32 || y1.len() != 32 || x1.len() != 32 { - return Ok(NativeResult::err(0.into(), E_INVALID_COORDINATE)); + return Ok(NativeResult::err(0.into(), E_INVALID_INPUT_SIZE)); } let cost = gas_params.base + (gas_params.per_byte * NumBytes::new(128_u64)); @@ -62,11 +150,46 @@ pub fn native_ec_add( } /*************************************************************************************************** - * native fun ec_pairing - * Implementation of the Move native function `ec_pairing(data: vector): vector` - * gas cost: ec_pairing_cost_base | base cost for function call and fixed opers - * + ec_pairing_data_cost_per_byte * msg.len() | cost depends on length of message - * + ec_pairing_data_cost_per_block * num_blocks | cost depends on number of blocks in message + * native function `ec_mul(x1: vector, y1: vector, s: vector): (vector, vector)` + **************************************************************************************************/ +pub fn native_ec_mul( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 3); + + let s = pop_arg!(args, Vec); + let y1 = pop_arg!(args, Vec); + let x1 = pop_arg!(args, Vec); + + if s.len() != 32 || y1.len() != 32 || x1.len() != 32 { + return Ok(NativeResult::err(0.into(), E_INVALID_INPUT_SIZE)); + } + + let cost = gas_params.base + (gas_params.per_byte * NumBytes::new(96_u64)); + + let mut data: Vec = Vec::new(); + data.extend(x1); + data.extend(y1); + data.extend(s); + + match bn128::run_mul(&data, 0, 10) { + Ok((_, mul)) => { + let (mul_x, mul_y) = mul.split_at(32); + let mul_x = Value::vector_u8(mul_x.to_vec()); + let mul_y = Value::vector_u8(mul_y.to_vec()); + Ok(NativeResult::ok(cost, smallvec![mul_x, mul_y])) + } + + Err(_) => Ok(NativeResult::err(cost, E_EC_MUL_FAILED)), + } +} + +/*************************************************************************************************** + * native function `ec_pairing(data: vector): vector` **************************************************************************************************/ pub fn native_ec_pairing( gas_params: &FromBytesGasParameters, @@ -90,6 +213,95 @@ pub fn native_ec_pairing( } } +/*************************************************************************************************** + * native function `blake2f(rounds: vector, h: vector, m: vector, t: vector, f: vector): vector` + **************************************************************************************************/ +pub fn native_blake2f( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 5); + + let f = pop_arg!(args, Vec); + let t = pop_arg!(args, Vec); + let m = pop_arg!(args, Vec); + let h = pop_arg!(args, Vec); + let rounds = pop_arg!(args, Vec); + + if rounds.len() != 4 || h.len() != 64 || m.len() != 128 || t.len() != 16 || f.len() != 1 { + return Ok(NativeResult::err(0.into(), E_INVALID_INPUT_SIZE)); + } + + let cost = gas_params.base + (gas_params.per_byte * NumBytes::new(213_u64)); + + let mut data: Vec = Vec::new(); + data.extend(rounds); + data.extend(h); + data.extend(m); + data.extend(t); + data.extend(f); + + match blake2::run(&data.into(), 100_000_000_000) { + Ok((_, out)) => Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(out.to_vec())], + )), + Err(_) => Ok(NativeResult::err(cost, E_EC_PAIRING_FAILED)), + } +} + +/*************************************************************************************************** + * native function `point_evaluation(versioned_hash: vector, x: vector, y: vector, commitment: vector, proof: vector): (vector, vector)` + **************************************************************************************************/ +pub fn native_point_evaluation( + gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 5); + + let proof = pop_arg!(args, Vec); + let commitment = pop_arg!(args, Vec); + let y = pop_arg!(args, Vec); + let x = pop_arg!(args, Vec); + let versioned_hash = pop_arg!(args, Vec); + + if versioned_hash.len() != 32 + || x.len() != 32 + || y.len() != 32 + || commitment.len() != 48 + || proof.len() != 48 + { + return Ok(NativeResult::err(0.into(), E_INVALID_INPUT_SIZE)); + } + + let cost = gas_params.base + (gas_params.per_byte * NumBytes::new(192_u64)); + + let mut data: Vec = Vec::new(); + data.extend(versioned_hash); + data.extend(x); + data.extend(y); + data.extend(commitment); + data.extend(proof); + let env = Env::default(); + + match kzg_point_evaluation::run(&data.into(), 100_000_000_000, &env) { + Ok((_, output)) => { + let (output_f, output_b) = output.split_at(32); + let output_f = Value::vector_u8(output_f.to_vec()); + let output_b = Value::vector_u8(output_b.to_vec()); + Ok(NativeResult::ok(cost, smallvec![output_f, output_b])) + } + + Err(_) => Ok(NativeResult::err(cost, E_POINT_EVALUATION_FAILED)), + } +} + #[derive(Debug, Clone)] pub struct FromBytesGasParameters { pub base: InternalGas, @@ -111,26 +323,47 @@ impl FromBytesGasParameters { #[derive(Debug, Clone)] pub struct GasParameters { + pub modexp: FromBytesGasParameters, + pub ec_recover: FromBytesGasParameters, pub ec_add: FromBytesGasParameters, + pub ec_mul: FromBytesGasParameters, pub ec_pairing: FromBytesGasParameters, + pub blake2f: FromBytesGasParameters, + pub point_evaluation: FromBytesGasParameters, } impl GasParameters { pub fn zeros() -> Self { Self { + modexp: FromBytesGasParameters::zeros(), + ec_recover: FromBytesGasParameters::zeros(), ec_add: FromBytesGasParameters::zeros(), + ec_mul: FromBytesGasParameters::zeros(), ec_pairing: FromBytesGasParameters::zeros(), + blake2f: FromBytesGasParameters::zeros(), + point_evaluation: FromBytesGasParameters::zeros(), } } } pub fn make_all(gas_params: GasParameters) -> impl Iterator { let natives = [ + ("modexp", make_native(gas_params.modexp, native_modexp)), + ( + "ec_recover", + make_native(gas_params.ec_recover, native_ec_recover), + ), ("ec_add", make_native(gas_params.ec_add, native_ec_add)), + ("ec_mul", make_native(gas_params.ec_mul, native_ec_mul)), ( "ec_pairing", make_native(gas_params.ec_pairing, native_ec_pairing), ), + ("blake2f", make_native(gas_params.blake2f, native_blake2f)), + ( + "point_evaluation", + make_native(gas_params.point_evaluation, native_point_evaluation), + ), ]; make_module_natives(natives) diff --git a/frameworks/rooch-framework/src/natives/gas_parameter/evm.rs b/frameworks/rooch-framework/src/natives/gas_parameter/evm.rs index a68482193b..253fcdc6af 100644 --- a/frameworks/rooch-framework/src/natives/gas_parameter/evm.rs +++ b/frameworks/rooch-framework/src/natives/gas_parameter/evm.rs @@ -5,8 +5,18 @@ use crate::natives::gas_parameter::native::MUL; use moveos_stdlib::natives::moveos_stdlib::evm::GasParameters; crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasParameters, "evm", [ + [.modexp.base, "modexp.base", 1000 * MUL], + [.modexp.per_byte, "modexp.per_byte", 30 * MUL], + [.ec_recover.base, "ec_recover.base", 1000 * MUL], + [.ec_recover.per_byte, "ec_recover.per_byte", 30 * MUL], [.ec_add.base, "ec_add.base", 1000 * MUL], [.ec_add.per_byte, "ec_add.per_byte", 30 * MUL], + [.ec_mul.base, "ec_mul.base", 1000 * MUL], + [.ec_mul.per_byte, "ec_mul.per_byte", 30 * MUL], [.ec_pairing.base, "ec_pairing.base", 1000 * MUL], [.ec_pairing.per_byte, "ec_pairing.per_byte", 30 * MUL], + [.blake2f.base, "blake2f.base", 1000 * MUL], + [.blake2f.per_byte, "blake2f.per_byte", 30 * MUL], + [.point_evaluation.base, "point_evaluation.base", 1000 * MUL], + [.point_evaluation.per_byte, "point_evaluation.per_byte", 30 * MUL], ]);