diff --git a/evm/Cargo.toml b/evm/Cargo.toml index e8dcfdfe..1e22ef33 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -21,9 +21,11 @@ rand = "0.8.5" rand_chacha = "0.3.1" rlp = "0.5.1" keccak-rust = { git = "https://github.com/npwardberkeley/keccak-rust" } +keccak-hash = "0.9.0" [dev-dependencies] hex-literal = "0.3.4" +hex = "0.4.3" [features] asmtools = ["hex"] diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 0ac6439b..81243e6c 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -28,6 +28,12 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/curve_mul.asm"), include_str!("asm/curve_add.asm"), include_str!("asm/moddiv.asm"), + include_str!("asm/secp256k1/curve_mul.asm"), + include_str!("asm/secp256k1/curve_add.asm"), + include_str!("asm/secp256k1/moddiv.asm"), + include_str!("asm/secp256k1/lift_x.asm"), + include_str!("asm/secp256k1/inverse_scalar.asm"), + include_str!("asm/ecrecover.asm"), include_str!("asm/storage_read.asm"), include_str!("asm/storage_write.asm"), ]; @@ -38,15 +44,9 @@ pub(crate) fn combined_kernel() -> Kernel { #[cfg(test)] mod tests { - use std::str::FromStr; - - use anyhow::Result; - use ethereum_types::U256; use log::debug; - use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::combined_kernel; - use crate::cpu::kernel::interpreter::run; #[test] fn make_kernel() { @@ -58,171 +58,4 @@ mod tests { let kernel = combined_kernel(); debug!("Total kernel size: {} bytes", kernel.code.len()); } - - fn u256ify<'a>(hexes: impl IntoIterator) -> Result> { - Ok(hexes - .into_iter() - .map(U256::from_str) - .collect::, _>>()?) - } - #[test] - fn test_exp() -> Result<()> { - // Make sure we can parse and assemble the entire kernel. - let kernel = combined_kernel(); - let exp = kernel.global_labels["exp"]; - let mut rng = thread_rng(); - let a = U256([0; 4].map(|_| rng.gen())); - let b = U256([0; 4].map(|_| rng.gen())); - - // Random input - let initial_stack = vec![U256::from_str("0xdeadbeef")?, b, a]; - let stack_with_kernel = run(&kernel.code, exp, initial_stack).stack; - let initial_stack = vec![b, a]; - let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP - let stack_with_opcode = run(&code, 0, initial_stack).stack; - assert_eq!(stack_with_kernel, stack_with_opcode); - - // 0 base - let initial_stack = vec![U256::from_str("0xdeadbeef")?, b, U256::zero()]; - let stack_with_kernel = run(&kernel.code, exp, initial_stack).stack; - let initial_stack = vec![b, U256::zero()]; - let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP - let stack_with_opcode = run(&code, 0, initial_stack).stack; - assert_eq!(stack_with_kernel, stack_with_opcode); - - // 0 exponent - let initial_stack = vec![U256::from_str("0xdeadbeef")?, U256::zero(), a]; - let stack_with_kernel = run(&kernel.code, exp, initial_stack).stack; - let initial_stack = vec![U256::zero(), a]; - let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP - let stack_with_opcode = run(&code, 0, initial_stack).stack; - assert_eq!(stack_with_kernel, stack_with_opcode); - - Ok(()) - } - - #[test] - fn test_ec_ops() -> Result<()> { - // Make sure we can parse and assemble the entire kernel. - let kernel = combined_kernel(); - let ec_add = kernel.global_labels["ec_add"]; - let ec_double = kernel.global_labels["ec_double"]; - let ec_mul = kernel.global_labels["ec_mul"]; - let identity = ("0x0", "0x0"); - let invalid = ("0x0", "0x3"); // Not on curve - let point0 = ( - "0x1feee7ec986e198890cb83be8b8ba09ee953b3f149db6d9bfdaa5c308a33e58d", - "0x2051cc9a9edd46231604fd88f351e95ec72a285be93e289ac59cb48561efb2c6", - ); - let point1 = ( - "0x15b64d0a5f329fb672029298be8050f444626e6de11903caffa74b388075be1b", - "0x2d9e07340bd5cd7b70687b98f2500ff930a89a30d7b6a3e04b1b4d345319d234", - ); - // point2 = point0 + point1 - let point2 = ( - "0x18659c0e0a8fedcb8747cf463fc7cfa05f667d84e771d0a9521fc1a550688f0c", - "0x283ed10b42703e187e7a808aeb45c6b457bc4cc7d704e53b3348a1e3b0bfa55b", - ); - // point3 = 2 * point0 - let point3 = ( - "0x17da2b7b1a01c8dfdf0f5a6415833c7d755d219aa7e2c4cd0ac83d87d0ca4217", - "0xc9ace9de14aac8114541b50c19320eb40f0eeac3621526d9e34dbcf4c3a6c0f", - ); - let s = "0xabb2a34c0e7956cfe6cef9ddb7e810c45ea19a6ebadd79c21959af09f5ba480a"; - // point4 = s * point0 - let point4 = ( - "0xe519344959cc17021fe98878f947f5c1b1675325533a620c1684cfa6367e6c0", - "0x7496a7575b0b6a821e19ce780ecc3e0b156e605327798693defeb9f265b7a6f", - ); - - // Standard addition #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([point2.1, point2.0])?); - // Standard addition #2 - let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([point2.1, point2.0])?); - - // Standard doubling #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([point3.1, point3.0])?); - // Standard doubling #2 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run(&kernel.code, ec_double, initial_stack).stack; - assert_eq!(stack, u256ify([point3.1, point3.0])?); - // Standard doubling #3 - let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; - let stack = run(&kernel.code, ec_mul, initial_stack).stack; - assert_eq!(stack, u256ify([point3.1, point3.0])?); - - // Addition with identity #1 - let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([point1.1, point1.0])?); - // Addition with identity #2 - let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([point1.1, point1.0])?); - // Addition with identity #3 - let initial_stack = - u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([identity.1, identity.0])?); - - // Addition with invalid point(s) #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, invalid.1, invalid.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - // Addition with invalid point(s) #2 - let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, point0.1, point0.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - // Addition with invalid point(s) #3 - let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, identity.1, identity.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - // Addition with invalid point(s) #4 - let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, invalid.1, invalid.0])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - - // Scalar multiplication #1 - let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; - let stack = run(&kernel.code, ec_mul, initial_stack).stack; - assert_eq!(stack, u256ify([point4.1, point4.0])?); - // Scalar multiplication #2 - let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; - let stack = run(&kernel.code, ec_mul, initial_stack).stack; - assert_eq!(stack, u256ify([identity.1, identity.0])?); - // Scalar multiplication #3 - let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; - let stack = run(&kernel.code, ec_mul, initial_stack).stack; - assert_eq!(stack, u256ify([point0.1, point0.0])?); - // Scalar multiplication #4 - let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; - let stack = run(&kernel.code, ec_mul, initial_stack).stack; - assert_eq!(stack, u256ify([identity.1, identity.0])?); - // Scalar multiplication #5 - let initial_stack = u256ify(["0xdeadbeef", s, invalid.1, invalid.0])?; - let stack = run(&kernel.code, ec_mul, initial_stack).stack; - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - - // Multiple calls - let ec_mul_hex = format!("0x{:x}", ec_mul); - let initial_stack = u256ify([ - "0xdeadbeef", - s, - &ec_mul_hex, - identity.1, - identity.0, - point0.1, - point0.0, - ])?; - let stack = run(&kernel.code, ec_add, initial_stack).stack; - assert_eq!(stack, u256ify([point4.1, point4.0])?); - - Ok(()) - } } diff --git a/evm/src/cpu/kernel/asm/basic_macros.asm b/evm/src/cpu/kernel/asm/basic_macros.asm index 9e884fea..db53bff5 100644 --- a/evm/src/cpu/kernel/asm/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/basic_macros.asm @@ -47,6 +47,26 @@ // stack: (pred != 0) * nz + (pred == 0) * z %endmacro +// If pred, yields z; otherwise, yields nz +// Assumes pred is boolean (either 0 or 1). +%macro select_bool + // stack: pred, nz, z + dup1 + // stack: pred, pred, nz, z + iszero + // stack: notpred, pred, nz, z + swap3 + // stack: z, pred, nz, notpred + mul + // stack: pred * z, nz, notpred + swap2 + // stack: notpred, nz, pred * z + mul + // stack: notpred * nz, pred * z + add + // stack: notpred * nz + pred * z +%endmacro + %macro square // stack: x dup1 diff --git a/evm/src/cpu/kernel/asm/curve_mul.asm b/evm/src/cpu/kernel/asm/curve_mul.asm index 246946d9..d976d9d5 100644 --- a/evm/src/cpu/kernel/asm/curve_mul.asm +++ b/evm/src/cpu/kernel/asm/curve_mul.asm @@ -14,7 +14,7 @@ global ec_mul: // stack: x, y, x, y, s, retdest %ec_isidentity // stack: (x,y)==(0,0), x, y, s, retdest - %jumpi(ret_zero) + %jumpi(ret_zero_ec_mul) // stack: x, y, s, retdest DUP2 // stack: y, x, y, s, retdest @@ -35,7 +35,7 @@ ec_mul_valid_point: // stack: s, x, y, s, retdest %jumpi(step_case) // stack: x, y, s, retdest - %jump(ret_zero) + %jump(ret_zero_ec_mul) step_case: JUMPDEST @@ -100,7 +100,7 @@ odd_scalar: // stack: x', y', x, y, retdest %jump(ec_add_valid_points) -ret_zero: +global ret_zero_ec_mul: JUMPDEST // stack: x, y, s, retdest %pop3 diff --git a/evm/src/cpu/kernel/asm/ecrecover.asm b/evm/src/cpu/kernel/asm/ecrecover.asm new file mode 100644 index 00000000..d0994054 --- /dev/null +++ b/evm/src/cpu/kernel/asm/ecrecover.asm @@ -0,0 +1,205 @@ +// ecrecover precompile. +global ecrecover: + JUMPDEST + // stack: hash, v, r, s, retdest + + // Check if inputs are valid. + %ecrecover_input_check + // stack: isValid(v,r,s), hash, v, r, s, retdest + + // Lift r to an elliptic curve point if possible. + SWAP2 + // stack: v, hash, isValid(v,r,s), r, s, retdest + DUP4 + // stack: r, v, hash, isValid(v,r,s), r, s, retdest + + // Compute v-27 which gives the parity of the y-coordinate of the lifted point. + SWAP1 + // stack: v, r, hash, isValid(v,r,s), r, s, retdest + PUSH 27 + // stack: 27, v, r, hash, isValid(v,r,s), r, s, retdest + SWAP1 + // stack: v, 27, r, hash, isValid(v,r,s), r, s, retdest + SUB + // stack: v - 27, r, hash, isValid(v,r,s), r, s, retdest + SWAP1 + // stack: r, v - 27, hash, isValid(v,r,s), r, s, retdest + %secp_lift_x + // stack: y, sqrtOk, hash, isValid(v,r,s), r, s, retdest + + // If inputs are invalid or lifting fails, abort. + SWAP3 + // stack: isValid(v,r,s), sqrtOk, hash, y, r, s, retdest + AND + // stack: isValid(v,r,s) & sqrtOk, hash, y, r, s, retdest + %jumpi(ecrecover_valid_input) + // stack: hash, y, r, s, retdest + %pop4 + // stack: retdest + %ecrecover_invalid_input + +// ecrecover precompile. +// Assumption: Inputs are valid. +// Pseudo-code: +// let P = lift_x(r, recovery_id); +// let r_inv = r.inverse(); +// let u1 = s * r_inv; +// let u2 = -hash * r_inv; +// return u1*P + u2*GENERATOR; +ecrecover_valid_input: + JUMPDEST + // stack: hash, y, r, s, retdest + + // Compute u1 = s * r^(-1) + SWAP1 + // stack: y, hash, r, s, retdest + DUP3 + // stack: r, y, hash, x, s, retdest (r=x) + %inverse_secp_scalar + // stack: r^(-1), y, hash, x, s, retdest + DUP1 + // stack: r^(-1), r^(-1), y, hash, x, s, retdest + SWAP5 + // stack: s, r^(-1), y, hash, x, r^(-1), retdest + %mulmodn_secp_scalar + // stack: u1, y, hash, x, r^(-1), retdest + + + // Compute (X,Y) = u1 * (x,y) + PUSH ecrecover_with_first_point + // stack: ecrecover_with_first_point, u1, y, hash, x, r^(-1), retdest + SWAP1 + // stack: u1, ecrecover_with_first_point, y, hash, x, r^(-1), retdest + SWAP2 + // stack: y, ecrecover_with_first_point, u1, hash, x, r^(-1), retdest + SWAP1 + // stack: ecrecover_with_first_point, y, u1, hash, x, r^(-1), retdest + SWAP3 + // stack: hash, y, u1, ecrecover_with_first_point, x, r^(-1), retdest + SWAP4 + // stack: x, y, u1, ecrecover_with_first_point, hash, r^(-1), retdest + %jump(ec_mul_valid_point_secp) + +// ecrecover precompile. +// Assumption: (X,Y) = u1 * P. Result is (X,Y) + u2*GENERATOR +ecrecover_with_first_point: + JUMPDEST + // stack: X, Y, hash, r^(-1), retdest + %secp_scalar + // stack: p, X, Y, hash, r^(-1), retdest + SWAP1 + // stack: X, p, Y, hash, r^(-1), retdest + SWAP4 + // stack: r^(-1), p, Y, hash, X, retdest + SWAP2 + // stack: Y, p, r^(-1), hash, X, retdest + SWAP3 + // stack: hash, p, r^(-1), Y, X, retdest + + // Compute u2 = -hash * r^(-1) + MOD + // stack: hash%p, r^(-1), Y, X, retdest + %secp_scalar + // stack: p, hash%p, r^(-1), Y, X, retdest + SUB + // stack: -hash, r^(-1), Y, X, retdest + %mulmodn_secp_scalar + // stack: u2, Y, X, retdest + + // Compute u2 * GENERATOR and chain the call to `ec_mul` with a call to `ec_add` to compute PUBKEY = (X,Y) + u2 * GENERATOR, + // and a call to `final_hashing` to get the final result `SHA3(PUBKEY)[-20:]`. + PUSH final_hashing + // stack: final_hashing, u2, Y, X, retdest + SWAP3 + // stack: X, u2, Y, final_hashing, retdest + PUSH ec_add_valid_points_secp + // stack: ec_add_valid_points_secp, X, u2, Y, final_hashing, retdest + SWAP1 + // stack: X, ec_add_valid_points_secp, u2, Y, final_hashing, retdest + PUSH 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 // x-coordinate of generator + // stack: Gx, X, ec_add_valid_points_secp, u2, Y, final_hashing, retdest + SWAP1 + // stack: X, Gx, ec_add_valid_points_secp, u2, Y, final_hashing, retdest + PUSH 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 // y-coordinate of generator + // stack: Gy, X, Gx, ec_add_valid_points_secp, u2, Y, final_hashing, retdest + SWAP1 + // stack: X, Gy, Gx, ec_add_valid_points_secp, u2, Y, final_hashing, retdest + SWAP4 + // stack: u2, Gy, Gx, ec_add_valid_points_secp, X, Y, final_hashing, retdest + SWAP2 + // stack: Gx, Gy, u2, ec_add_valid_points_secp, X, Y, final_hashing, retdest + %jump(ec_mul_valid_point_secp) + +// TODO +final_hashing: + JUMPDEST + PUSH 0xdeadbeef + JUMP + +// Check if v, r, and s are in correct form. +// Returns r < N & r!=0 & s < N & s!=0 & (v==28 || v==27). +%macro ecrecover_input_check + // stack: hash, v, r, s, retdest + DUP2 + // stack: v, hash, v, r, s, retdest + PUSH 27 + // stack: 27, v, hash, v, r, s, retdest + EQ + // stack: v==27, hash, v, r, s, retdest + DUP3 + // stack: v, v==27, hash, v, r, s, retdest + PUSH 28 + // stack: 28, v, v==27, hash, v, r, s, retdest + EQ + // stack: v==28, v==27, hash, v, r, s, retdest + OR + // stack: (v==28 || v==27), hash, v, r, s, retdest + DUP5 + // stack: s, (v==28 || v==27), hash, v, r, s, retdest + %secp_is_out_of_bounds + // stack: (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest + DUP5 + // stack: r, (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest + %secp_is_out_of_bounds + // stack: (r >= N || r==0), (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest + OR + // stack: (r >= N || r==0 || s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest + ISZERO + // stack: (r < N & r!=0 & s < N & s!=0), (v==28 || v==27), hash, v, r, s, retdest + AND + // stack: r < N & r!=0 & s < N & s!=0 & (v==28 || v==27), hash, v, r, s, retdest +%endmacro + +%macro secp_is_out_of_bounds + // stack: x + DUP1 + // stack: x, x + ISZERO + // stack: x==0, x + SWAP1 + // stack: x, x==0 + %secp_scalar + // stack: N, x, x==0 + SWAP1 + // stack: x, N, x==0 + LT + // stack: x < N, x==0 + ISZERO + // stack: x >= N, x==0 + OR + // stack: x >= N || x==0 +%endmacro + +%macro secp_scalar + PUSH 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 +%endmacro + +// Return u256::MAX which is used to indicate the input was invalid. +%macro ecrecover_invalid_input + // stack: retdest + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + // stack: u256::MAX, retdest + SWAP1 + // stack: retdest, u256::MAX + JUMP +%endmacro diff --git a/evm/src/cpu/kernel/asm/secp256k1/curve_add.asm b/evm/src/cpu/kernel/asm/secp256k1/curve_add.asm new file mode 100644 index 00000000..7f9c1fff --- /dev/null +++ b/evm/src/cpu/kernel/asm/secp256k1/curve_add.asm @@ -0,0 +1,310 @@ +// #define N 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 // Secp256k1 scalar field order + +// Secp256k1 elliptic curve addition. +// Assumption: (x0,y0) and (x1,y1) are valid points. +global ec_add_valid_points_secp: + JUMPDEST + // stack: x0, y0, x1, y1, retdest + + // Check if the first point is the identity. + DUP2 + // stack: y0, x0, y0, x1, y1, retdest + DUP2 + // stack: x0, y0, x0, y0, x1, y1, retdest + %ec_isidentity + // stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest + %jumpi(ec_add_first_zero) + // stack: x0, y0, x1, y1, retdest + + // Check if the first point is the identity. + DUP4 + // stack: y1, x0, y0, x1, y1, retdest + DUP4 + // stack: x1, y1, x0, y0, x1, y1, retdest + %ec_isidentity + // stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest + %jumpi(ec_add_snd_zero) + // stack: x0, y0, x1, y1, retdest + + // Check if both points have the same x-coordinate. + DUP3 + // stack: x1, x0, y0, x1, y1, retdest + DUP2 + // stack: x0, x1, x0, y0, x1, y1, retdest + EQ + // stack: x0 == x1, x0, y0, x1, y1, retdest + %jumpi(ec_add_equal_first_coord) + // stack: x0, y0, x1, y1, retdest + + // Otherwise, we can use the standard formula. + // Compute lambda = (y0 - y1)/(x0 - x1) + DUP4 + // stack: y1, x0, y0, x1, y1, retdest + DUP3 + // stack: y0, y1, x0, y0, x1, y1, retdest + %submod_secp_base + // stack: y0 - y1, x0, y0, x1, y1, retdest + DUP4 + // stack: x1, y0 - y1, x0, y0, x1, y1, retdest + DUP3 + // stack: x0, x1, y0 - y1, x0, y0, x1, y1, retdest + %submod_secp_base + // stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest + %moddiv_secp_base + // stack: lambda, x0, y0, x1, y1, retdest + %jump(ec_add_valid_points_with_lambda) + +// Secp256k1 elliptic curve addition. +// Assumption: (x0,y0) == (0,0) +ec_add_first_zero: + JUMPDEST + // stack: x0, y0, x1, y1, retdest + + // Just return (x1,y1) + %pop2 + // stack: x1, y1, retdest + SWAP1 + // stack: y1, x1, retdest + SWAP2 + // stack: retdest, x1, y1 + JUMP + +// Secp256k1 elliptic curve addition. +// Assumption: (x1,y1) == (0,0) +ec_add_snd_zero: + JUMPDEST + // stack: x0, y0, x1, y1, retdest + + // Just return (x1,y1) + SWAP2 + // stack: x1, y0, x0, y1, retdest + POP + // stack: y0, x0, y1, retdest + SWAP2 + // stack: y1, x0, y0, retdest + POP + // stack: x0, y0, retdest + SWAP1 + // stack: y0, x0, retdest + SWAP2 + // stack: retdest, x0, y0 + JUMP + +// Secp256k1 elliptic curve addition. +// Assumption: lambda = (y0 - y1)/(x0 - x1) +ec_add_valid_points_with_lambda: + JUMPDEST + // stack: lambda, x0, y0, x1, y1, retdest + + // Compute x2 = lambda^2 - x1 - x0 + DUP2 + // stack: x0, lambda, x0, y0, x1, y1, retdest + DUP5 + // stack: x1, x0, lambda, x0, y0, x1, y1, retdest + %secp_base + // stack: N, x1, x0, lambda, x0, y0, x1, y1, retdest + DUP4 + // stack: lambda, N, x1, x0, lambda, x0, y0, x1, y1, retdest + DUP1 + // stack: lambda, lambda, N, x1, x0, lambda, x0, y0, x1, y1, retdest + MULMOD + // stack: lambda^2, x1, x0, lambda, x0, y0, x1, y1, retdest + %submod_secp_base + // stack: lambda^2 - x1, x0, lambda, x0, y0, x1, y1, retdest + %submod_secp_base + // stack: x2, lambda, x0, y0, x1, y1, retdest + + // Compute y2 = lambda*(x1 - x2) - y1 + %secp_base + // stack: N, x2, lambda, x0, y0, x1, y1, retdest + DUP2 + // stack: x2, N, x2, lambda, x0, y0, x1, y1, retdest + DUP7 + // stack: x1, x2, N, x2, lambda, x0, y0, x1, y1, retdest + %submod_secp_base + // stack: x1 - x2, N, x2, lambda, x0, y0, x1, y1, retdest + DUP4 + // stack: lambda, x1 - x2, N, x2, lambda, x0, y0, x1, y1, retdest + MULMOD + // stack: lambda * (x1 - x2), x2, lambda, x0, y0, x1, y1, retdest + DUP7 + // stack: y1, lambda * (x1 - x2), x2, lambda, x0, y0, x1, y1, retdest + SWAP1 + // stack: lambda * (x1 - x2), y1, x2, lambda, x0, y0, x1, y1, retdest + %submod_secp_base + // stack: y2, x2, lambda, x0, y0, x1, y1, retdest + + // Return x2,y2 + SWAP5 + // stack: x1, x2, lambda, x0, y0, y2, y1, retdest + POP + // stack: x2, lambda, x0, y0, y2, y1, retdest + SWAP5 + // stack: y1, lambda, x0, y0, y2, x2, retdest + %pop4 + // stack: y2, x2, retdest + SWAP2 + // stack: retdest, x2, y2 + JUMP + +// Secp256k1 elliptic curve addition. +// Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1 +ec_add_equal_first_coord: + JUMPDEST + // stack: x0, y0, x1, y1, retdest with x0 == x1 + + // Check if the points are equal + DUP2 + // stack: y0, x0, y0, x1, y1, retdest + DUP5 + // stack: y1, y0, x0, y0, x1, y1, retdest + EQ + // stack: y1 == y0, x0, y0, x1, y1, retdest + %jumpi(ec_add_equal_points) + // stack: x0, y0, x1, y1, retdest + + // Otherwise, one is the negation of the other so we can return (0,0). + %pop4 + // stack: retdest + PUSH 0 + // stack: 0, retdest + PUSH 0 + // stack: 0, 0, retdest + SWAP2 + // stack: retdest, 0, 0 + JUMP + + +// Secp256k1 elliptic curve addition. +// Assumption: x0 == x1 and y0 == y1 +// Standard doubling formula. +ec_add_equal_points: + JUMPDEST + // stack: x0, y0, x1, y1, retdest + + // Compute lambda = 3/2 * x0^2 / y0 + %secp_base + // stack: N, x0, y0, x1, y1, retdest + %secp_base + // stack: N, N, x0, y0, x1, y1, retdest + DUP3 + // stack: x0, N, N, x0, y0, x1, y1, retdest + DUP1 + // stack: x0, x0, N, N, x0, y0, x1, y1, retdest + MULMOD + // stack: x0^2, N, x0, y0, x1, y1, retdest with + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe19 // 3/2 in the base field + // stack: 3/2, x0^2, N, x0, y0, x1, y1, retdest + MULMOD + // stack: 3/2 * x0^2, x0, y0, x1, y1, retdest + DUP3 + // stack: y0, 3/2 * x0^2, x0, y0, x1, y1, retdest + %moddiv_secp_base + // stack: lambda, x0, y0, x1, y1, retdest + %jump(ec_add_valid_points_with_lambda) + +// Secp256k1 elliptic curve doubling. +// Assumption: (x0,y0) is a valid point. +// Standard doubling formula. +global ec_double_secp: + JUMPDEST + // stack: x0, y0, retdest + DUP2 + // stack: y0, x0, y0, retdest + DUP2 + // stack: x0, y0, x0, y0, retdest + %jump(ec_add_equal_points) + +// Push the order of the Secp256k1 scalar field. +%macro secp_base + PUSH 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f +%endmacro + +// Modular subtraction. Subtraction x-y underflows iff x Result<()> { + // Make sure we can parse and assemble the entire kernel. + let kernel = combined_kernel(); + let ec_add = kernel.global_labels["ec_add"]; + let ec_double = kernel.global_labels["ec_double"]; + let ec_mul = kernel.global_labels["ec_mul"]; + let identity = ("0x0", "0x0"); + let invalid = ("0x0", "0x3"); // Not on curve + let point0 = ( + "0x1feee7ec986e198890cb83be8b8ba09ee953b3f149db6d9bfdaa5c308a33e58d", + "0x2051cc9a9edd46231604fd88f351e95ec72a285be93e289ac59cb48561efb2c6", + ); + let point1 = ( + "0x15b64d0a5f329fb672029298be8050f444626e6de11903caffa74b388075be1b", + "0x2d9e07340bd5cd7b70687b98f2500ff930a89a30d7b6a3e04b1b4d345319d234", + ); + // point2 = point0 + point1 + let point2 = ( + "0x18659c0e0a8fedcb8747cf463fc7cfa05f667d84e771d0a9521fc1a550688f0c", + "0x283ed10b42703e187e7a808aeb45c6b457bc4cc7d704e53b3348a1e3b0bfa55b", + ); + // point3 = 2 * point0 + let point3 = ( + "0x17da2b7b1a01c8dfdf0f5a6415833c7d755d219aa7e2c4cd0ac83d87d0ca4217", + "0xc9ace9de14aac8114541b50c19320eb40f0eeac3621526d9e34dbcf4c3a6c0f", + ); + let s = "0xabb2a34c0e7956cfe6cef9ddb7e810c45ea19a6ebadd79c21959af09f5ba480a"; + // point4 = s * point0 + let point4 = ( + "0xe519344959cc17021fe98878f947f5c1b1675325533a620c1684cfa6367e6c0", + "0x7496a7575b0b6a821e19ce780ecc3e0b156e605327798693defeb9f265b7a6f", + ); + + // Standard addition #1 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point2.1, point2.0])?); + // Standard addition #2 + let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point2.1, point2.0])?); + + // Standard doubling #1 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point3.1, point3.0])?); + // Standard doubling #2 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_double, initial_stack).stack; + assert_eq!(stack, u256ify([point3.1, point3.0])?); + // Standard doubling #3 + let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([point3.1, point3.0])?); + + // Addition with identity #1 + let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point1.1, point1.0])?); + // Addition with identity #2 + let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point1.1, point1.0])?); + // Addition with identity #3 + let initial_stack = + u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([identity.1, identity.0])?); + + // Addition with invalid point(s) #1 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, invalid.1, invalid.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, vec![U256::MAX, U256::MAX]); + // Addition with invalid point(s) #2 + let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, vec![U256::MAX, U256::MAX]); + // Addition with invalid point(s) #3 + let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, vec![U256::MAX, U256::MAX]); + // Addition with invalid point(s) #4 + let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, invalid.1, invalid.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, vec![U256::MAX, U256::MAX]); + + // Scalar multiplication #1 + let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([point4.1, point4.0])?); + // Scalar multiplication #2 + let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([identity.1, identity.0])?); + // Scalar multiplication #3 + let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([point0.1, point0.0])?); + // Scalar multiplication #4 + let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([identity.1, identity.0])?); + // Scalar multiplication #5 + let initial_stack = u256ify(["0xdeadbeef", s, invalid.1, invalid.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, vec![U256::MAX, U256::MAX]); + + // Multiple calls + let ec_mul_hex = format!("0x{:x}", ec_mul); + let initial_stack = u256ify([ + "0xdeadbeef", + s, + &ec_mul_hex, + identity.1, + identity.0, + point0.1, + point0.0, + ])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point4.1, point4.0])?); + + Ok(()) + } +} + +#[cfg(test)] +mod secp { + use anyhow::Result; + + use crate::cpu::kernel::aggregator::combined_kernel; + use crate::cpu::kernel::interpreter::run; + use crate::cpu::kernel::tests::u256ify; + + #[test] + fn test_ec_ops() -> Result<()> { + // Make sure we can parse and assemble the entire kernel. + let kernel = combined_kernel(); + let ec_add = kernel.global_labels["ec_add_valid_points_secp"]; + let ec_double = kernel.global_labels["ec_double_secp"]; + let ec_mul = kernel.global_labels["ec_mul_valid_point_secp"]; + let identity = ("0x0", "0x0"); + let point0 = ( + "0xc82ccceebd739e646631b7270ed8c33e96c4940b19db91eaf67da6ec92d109b", + "0xe0d241d2de832656c3eed78271bb06b5602d6473742c7c48a38b9f0350a76164", + ); + let point1 = ( + "0xbf26b1a7a46025d0a1787aa050d0bb83b8a4746010f873404389b8b23360919c", + "0x65adeff3fed1b22fa10279b5a25b96694a20bcbf6b718c0412f6d34a2e9bb924", + ); + // point2 = point0 + point1 + let point2 = ( + "0x191e8183402c6d6f5f22a9fe2a5ce17a7dd5184bd5d359c77189e9f714a18225", + "0xe23fbb6913de7449d92e4dfbe278e2874fac80d53bfeb8fb3400462b7bfaec74", + ); + // point3 = 2 * point0 + let point3 = ( + "0x7872498939b02197c2b6f0a0f5767f36551e43f910de472fbbff0538b21f5f45", + "0x294e15025d935438023a0e4056892abd6405fade13cf2b3131d8755be7cebad", + ); + let s = "0xa72ad7d8ce24135b5138f853d7a9896381c40523b5d1cf03072151f2af10e35e"; + // point4 = s * point0 + let point4 = ( + "0xd8bec38864f0fe56d429540e6de624afb8ddc7fba1f738337913922a30b96c14", + "0x5b086b2720ac39d173777bc36a49629c80c3a3e55e1c50527e60016d9be71318", + ); + + // Standard addition #1 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point2.1, point2.0])?); + // Standard addition #2 + let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point2.1, point2.0])?); + + // Standard doubling #1 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point3.1, point3.0])?); + // Standard doubling #2 + let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_double, initial_stack).stack; + assert_eq!(stack, u256ify([point3.1, point3.0])?); + // Standard doubling #3 + let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([point3.1, point3.0])?); + + // Addition with identity #1 + let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point1.1, point1.0])?); + // Addition with identity #2 + let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point1.1, point1.0])?); + // Addition with identity #3 + let initial_stack = + u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([identity.1, identity.0])?); + + // Scalar multiplication #1 + let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([point4.1, point4.0])?); + // Scalar multiplication #2 + let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([identity.1, identity.0])?); + // Scalar multiplication #3 + let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([point0.1, point0.0])?); + // Scalar multiplication #4 + let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; + let stack = run(&kernel.code, ec_mul, initial_stack).stack; + assert_eq!(stack, u256ify([identity.1, identity.0])?); + + // Multiple calls + let ec_mul_hex = format!("0x{:x}", ec_mul); + let initial_stack = u256ify([ + "0xdeadbeef", + s, + &ec_mul_hex, + identity.1, + identity.0, + point0.1, + point0.0, + ])?; + let stack = run(&kernel.code, ec_add, initial_stack).stack; + assert_eq!(stack, u256ify([point4.1, point4.0])?); + + Ok(()) + } +} diff --git a/evm/src/cpu/kernel/tests/ecrecover.rs b/evm/src/cpu/kernel/tests/ecrecover.rs new file mode 100644 index 00000000..5077d042 --- /dev/null +++ b/evm/src/cpu/kernel/tests/ecrecover.rs @@ -0,0 +1,103 @@ +use anyhow::Result; +use ethereum_types::U256; +use keccak_hash::keccak; + +use crate::cpu::kernel::aggregator::combined_kernel; +use crate::cpu::kernel::assembler::Kernel; +use crate::cpu::kernel::interpreter::run; +use crate::cpu::kernel::tests::u256ify; + +fn pubkey_to_addr(x: U256, y: U256) -> Vec { + let mut buf = [0; 64]; + x.to_big_endian(&mut buf[0..32]); + y.to_big_endian(&mut buf[32..64]); + let hash = keccak(buf); + hash.0[12..].to_vec() +} + +fn test_valid_ecrecover( + hash: &str, + v: &str, + r: &str, + s: &str, + expected: &str, + kernel: &Kernel, +) -> Result<()> { + let ecrecover = kernel.global_labels["ecrecover"]; + let initial_stack = u256ify([s, r, v, hash])?; + let stack = run(&kernel.code, ecrecover, initial_stack).stack; + let got = pubkey_to_addr(stack[1], stack[0]); + assert_eq!(got, hex::decode(&expected[2..]).unwrap()); + + Ok(()) +} + +fn test_invalid_ecrecover(hash: &str, v: &str, r: &str, s: &str, kernel: &Kernel) -> Result<()> { + let ecrecover = kernel.global_labels["ecrecover"]; + let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; + let stack = run(&kernel.code, ecrecover, initial_stack).stack; + assert_eq!(stack, vec![U256::MAX]); + + Ok(()) +} + +#[test] +fn test_ecrecover() -> Result<()> { + let kernel = combined_kernel(); + + test_valid_ecrecover( + "0x55f77e8909b1f1c9531c4a309bb2d40388e9ed4b87830c8f90363c6b36255fb9", + "0x1b", + "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", + "0x58351f48ce34bf134ee611fb5bf255a5733f0029561d345a7d46bfa344b60ac0", + "0x67f3c0Da351384838d7F7641AB0fCAcF853E1844", + &kernel, + )?; + test_valid_ecrecover( + "0x55f77e8909b1f1c9531c4a309bb2d40388e9ed4b87830c8f90363c6b36255fb9", + "0x1c", + "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", + "0x58351f48ce34bf134ee611fb5bf255a5733f0029561d345a7d46bfa344b60ac0", + "0xaA58436DeABb64982a386B2De1A8015AA28fCCc0", + &kernel, + )?; + test_valid_ecrecover( + "0x0", + "0x1c", + "0x1", + "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "0x3344c6f6eeCA588be132142DB0a32C71ABFAAe7B", + &kernel, + )?; + + test_invalid_ecrecover( + "0x0", + "0x42", // v not in {27,28} + "0x1", + "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + &kernel, + )?; + test_invalid_ecrecover( + "0x0", + "0x42", + "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", + "0x0", // s=0 + &kernel, + )?; + test_invalid_ecrecover( + "0x0", + "0x42", + "0x0", // r=0 + "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", + &kernel, + )?; + test_invalid_ecrecover( + "0x0", + "0x1c", + "0x3a18b21408d275dde53c0ea86f9c1982eca60193db0ce15008fa408d43024847", // r^3 + 7 isn't a square + "0x5db9745f44089305b2f2c980276e7025a594828d878e6e36dd2abd34ca6b9e3d", + &kernel, + )?; + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/exp.rs b/evm/src/cpu/kernel/tests/exp.rs new file mode 100644 index 00000000..b12b943e --- /dev/null +++ b/evm/src/cpu/kernel/tests/exp.rs @@ -0,0 +1,44 @@ +use std::str::FromStr; + +use anyhow::Result; +use ethereum_types::U256; +use rand::{thread_rng, Rng}; + +use crate::cpu::kernel::aggregator::combined_kernel; +use crate::cpu::kernel::interpreter::run; + +#[test] +fn test_exp() -> Result<()> { + // Make sure we can parse and assemble the entire kernel. + let kernel = combined_kernel(); + let exp = kernel.global_labels["exp"]; + let mut rng = thread_rng(); + let a = U256([0; 4].map(|_| rng.gen())); + let b = U256([0; 4].map(|_| rng.gen())); + + // Random input + let initial_stack = vec![U256::from_str("0xdeadbeef")?, b, a]; + let stack_with_kernel = run(&kernel.code, exp, initial_stack).stack; + let initial_stack = vec![b, a]; + let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP + let stack_with_opcode = run(&code, 0, initial_stack).stack; + assert_eq!(stack_with_kernel, stack_with_opcode); + + // 0 base + let initial_stack = vec![U256::from_str("0xdeadbeef")?, b, U256::zero()]; + let stack_with_kernel = run(&kernel.code, exp, initial_stack).stack; + let initial_stack = vec![b, U256::zero()]; + let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP + let stack_with_opcode = run(&code, 0, initial_stack).stack; + assert_eq!(stack_with_kernel, stack_with_opcode); + + // 0 exponent + let initial_stack = vec![U256::from_str("0xdeadbeef")?, U256::zero(), a]; + let stack_with_kernel = run(&kernel.code, exp, initial_stack).stack; + let initial_stack = vec![U256::zero(), a]; + let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP + let stack_with_opcode = run(&code, 0, initial_stack).stack; + assert_eq!(stack_with_kernel, stack_with_opcode); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs new file mode 100644 index 00000000..100ef377 --- /dev/null +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -0,0 +1,15 @@ +mod curve_ops; +mod ecrecover; +mod exp; + +use std::str::FromStr; + +use anyhow::Result; +use ethereum_types::U256; + +pub(crate) fn u256ify<'a>(hexes: impl IntoIterator) -> Result> { + Ok(hexes + .into_iter() + .map(U256::from_str) + .collect::, _>>()?) +}