Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/precompiles/blake2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ mod consts {

pub(super) struct Blake2F;

impl Blake2F {
pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 9);
}

impl Precompile for Blake2F {
fn required_gas(input: &[u8]) -> Result<u64, ExitError> {
let (int_bytes, _) = input.split_at(mem::size_of::<u32>());
Expand Down
9 changes: 9 additions & 0 deletions src/precompiles/bn128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ mod consts {
pub(super) const PAIR_ELEMENT_LEN: usize = 192;
}

/// bn128 precompile addresses
pub(super) mod addresses {
use crate::precompiles;

pub const ADD: [u8; 20] = precompiles::make_address(0, 6);
pub const MUL: [u8; 20] = precompiles::make_address(0, 7);
pub const PAIR: [u8; 20] = precompiles::make_address(0, 8);
}

/// Reads the `x` and `y` points from an input at a given position.
fn read_point(input: &[u8], pos: usize) -> Result<bn::G1, ExitError> {
use bn::{AffineG1, Fq, Group, G1};
Expand Down
8 changes: 8 additions & 0 deletions src/precompiles/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ mod consts {
/// SHA256 precompile.
pub struct SHA256;

impl SHA256 {
pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 2);
}

impl Precompile for SHA256 {
fn required_gas(input: &[u8]) -> Result<u64, ExitError> {
Ok(
Expand Down Expand Up @@ -67,6 +71,10 @@ impl Precompile for SHA256 {
/// RIPEMD160 precompile.
pub struct RIPEMD160;

impl RIPEMD160 {
pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 3);
}

impl Precompile for RIPEMD160 {
fn required_gas(input: &[u8]) -> Result<u64, ExitError> {
Ok(
Expand Down
4 changes: 4 additions & 0 deletions src/precompiles/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ mod consts {

pub struct Identity;

impl Identity {
pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 4);
}

impl Precompile for Identity {
fn required_gas(input: &[u8]) -> Result<u64, ExitError> {
Ok(
Expand Down
173 changes: 120 additions & 53 deletions src/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ use crate::precompiles::secp256k1::ECRecover;
use crate::prelude::{Address, Vec};
use evm::{Context, ExitError, ExitSucceed};

/// Exit to Ethereum precompile address (truncated to 8 bytes)
///
/// Address: `0xb0bd02f6a392af548bdf1cfaee5dfa0eefcc8eab`
/// This address is computed as: `&keccak("exitToEthereum")[12..]`
const EXIT_TO_ETHEREUM_ID: u64 = 17176159495920586411;

/// Exit to NEAR precompile address (truncated to 8 bytes)
///
/// Address: `0xe9217bc70b7ed1f598ddd3199e80b093fa71124f`
/// This address is computed as: `&keccak("exitToNear")[12..]`
const EXIT_TO_NEAR_ID: u64 = 11421322804619973199;

/// A precompile operation result.
type PrecompileResult = Result<(ExitSucceed, Vec<u8>, u64), ExitError>;

Expand Down Expand Up @@ -88,12 +76,12 @@ pub fn homestead_precompiles(
None => return Some(PrecompileResult::Err(ExitError::OutOfGas)),
};

match address.to_low_u64_be() {
1 => Some(ECRecover::run(input, target_gas, context)),
2 => Some(SHA256::run(input, target_gas, context)),
3 => Some(RIPEMD160::run(input, target_gas, context)),
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
match address.0 {
ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)),
SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)),
RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)),
ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)),
ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
Expand All @@ -111,17 +99,17 @@ pub fn byzantium_precompiles(
None => return Some(PrecompileResult::Err(ExitError::OutOfGas)),
};

match address.to_low_u64_be() {
1 => Some(ECRecover::run(input, target_gas, context)),
2 => Some(SHA256::run(input, target_gas, context)),
3 => Some(RIPEMD160::run(input, target_gas, context)),
4 => Some(Identity::run(input, target_gas, context)),
5 => Some(ModExp::<Byzantium>::run(input, target_gas, context)),
6 => Some(BN128Add::<Byzantium>::run(input, target_gas, context)),
7 => Some(BN128Mul::<Byzantium>::run(input, target_gas, context)),
8 => Some(BN128Pair::<Byzantium>::run(input, target_gas, context)),
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
match address.0 {
ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)),
SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)),
RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)),
Identity::ADDRESS => Some(Identity::run(input, target_gas, context)),
modexp::ADDRESS => Some(ModExp::<Byzantium>::run(input, target_gas, context)),
bn128::addresses::ADD => Some(BN128Add::<Byzantium>::run(input, target_gas, context)),
bn128::addresses::MUL => Some(BN128Mul::<Byzantium>::run(input, target_gas, context)),
bn128::addresses::PAIR => Some(BN128Pair::<Byzantium>::run(input, target_gas, context)),
ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)),
ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
Expand All @@ -139,18 +127,18 @@ pub fn istanbul_precompiles(
None => return Some(PrecompileResult::Err(ExitError::OutOfGas)),
};

match address.to_low_u64_be() {
1 => Some(ECRecover::run(input, target_gas, context)),
2 => Some(SHA256::run(input, target_gas, context)),
3 => Some(RIPEMD160::run(input, target_gas, context)),
4 => Some(Identity::run(input, target_gas, context)),
5 => Some(ModExp::<Byzantium>::run(input, target_gas, context)),
6 => Some(BN128Add::<Istanbul>::run(input, target_gas, context)),
7 => Some(BN128Mul::<Istanbul>::run(input, target_gas, context)),
8 => Some(BN128Pair::<Istanbul>::run(input, target_gas, context)),
9 => Some(Blake2F::run(input, target_gas, context)),
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
match address.0 {
ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)),
SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)),
RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)),
Identity::ADDRESS => Some(Identity::run(input, target_gas, context)),
modexp::ADDRESS => Some(ModExp::<Byzantium>::run(input, target_gas, context)),
bn128::addresses::ADD => Some(BN128Add::<Istanbul>::run(input, target_gas, context)),
bn128::addresses::MUL => Some(BN128Mul::<Istanbul>::run(input, target_gas, context)),
bn128::addresses::PAIR => Some(BN128Pair::<Istanbul>::run(input, target_gas, context)),
Blake2F::ADDRESS => Some(Blake2F::run(input, target_gas, context)),
ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)),
ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
Expand All @@ -168,20 +156,99 @@ pub fn berlin_precompiles(
None => return Some(PrecompileResult::Err(ExitError::OutOfGas)),
};

match address.to_low_u64_be() {
1 => Some(ECRecover::run(input, target_gas, context)),
2 => Some(SHA256::run(input, target_gas, context)),
3 => Some(RIPEMD160::run(input, target_gas, context)),
4 => Some(Identity::run(input, target_gas, context)),
5 => Some(ModExp::<Berlin>::run(input, target_gas, context)), // TODO gas changes
6 => Some(BN128Add::<Istanbul>::run(input, target_gas, context)),
7 => Some(BN128Mul::<Istanbul>::run(input, target_gas, context)),
8 => Some(BN128Pair::<Istanbul>::run(input, target_gas, context)),
9 => Some(Blake2F::run(input, target_gas, context)),
match address.0 {
ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)),
SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)),
RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)),
Identity::ADDRESS => Some(Identity::run(input, target_gas, context)),
modexp::ADDRESS => Some(ModExp::<Berlin>::run(input, target_gas, context)), // TODO gas changes
bn128::addresses::ADD => Some(BN128Add::<Istanbul>::run(input, target_gas, context)),
bn128::addresses::MUL => Some(BN128Mul::<Istanbul>::run(input, target_gas, context)),
bn128::addresses::PAIR => Some(BN128Pair::<Istanbul>::run(input, target_gas, context)),
Blake2F::ADDRESS => Some(Blake2F::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}

/// const fn for making an address by concatenating the bytes from two given numbers,
/// Note that 32 + 128 = 160 = 20 bytes (the length of an address). This function is used
/// as a convenience for specifying the addresses of the various precompiles.
const fn make_address(x: u32, y: u128) -> [u8; 20] {
let x_bytes = x.to_be_bytes();
let y_bytes = y.to_be_bytes();
[
x_bytes[0],
x_bytes[1],
x_bytes[2],
x_bytes[3],
y_bytes[0],
y_bytes[1],
y_bytes[2],
y_bytes[3],
y_bytes[4],
y_bytes[5],
y_bytes[6],
y_bytes[7],
y_bytes[8],
y_bytes[9],
y_bytes[10],
y_bytes[11],
y_bytes[12],
y_bytes[13],
y_bytes[14],
y_bytes[15],
]
Comment thread
artob marked this conversation as resolved.
}
Comment thread
artob marked this conversation as resolved.

#[cfg(test)]
mod tests {
use rand::Rng;

#[test]
fn test_precompile_addresses() {
assert_eq!(super::secp256k1::ECRecover::ADDRESS, u8_to_address(1));
assert_eq!(super::hash::SHA256::ADDRESS, u8_to_address(2));
assert_eq!(super::hash::RIPEMD160::ADDRESS, u8_to_address(3));
assert_eq!(super::identity::Identity::ADDRESS, u8_to_address(4));
assert_eq!(super::modexp::ADDRESS, u8_to_address(5));
assert_eq!(super::bn128::addresses::ADD, u8_to_address(6));
assert_eq!(super::bn128::addresses::MUL, u8_to_address(7));
assert_eq!(super::bn128::addresses::PAIR, u8_to_address(8));
assert_eq!(super::blake2::Blake2F::ADDRESS, u8_to_address(9));
}

#[test]
fn test_make_address() {
for i in 0..u8::MAX {
assert_eq!(super::make_address(0, i as u128), u8_to_address(i));
}

let mut rng = rand::thread_rng();
for _ in 0..u8::MAX {
let address: [u8; 20] = rng.gen();
let (x, y) = split_address(address);
assert_eq!(address, super::make_address(x, y))
}
}

fn u8_to_address(x: u8) -> [u8; 20] {
let mut bytes = [0u8; 20];
bytes[19] = x;
bytes
}

// Inverse function of `super::make_address`.
fn split_address(a: [u8; 20]) -> (u32, u128) {
let mut x_bytes = [0u8; 4];
let mut y_bytes = [0u8; 16];

x_bytes.copy_from_slice(&a[0..4]);
y_bytes.copy_from_slice(&a[4..20]);

(u32::from_be_bytes(x_bytes), u128::from_be_bytes(y_bytes))
}
}
2 changes: 2 additions & 0 deletions src/precompiles/modexp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::prelude::{PhantomData, Vec, U256};
use evm::{Context, ExitError, ExitSucceed};
use num::BigUint;

pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 5);

pub(super) struct ModExp<HF: HardFork>(PhantomData<HF>);

impl ModExp<Byzantium> {
Expand Down
29 changes: 23 additions & 6 deletions src/precompiles/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ pub fn is_valid_account_id(account_id: &[u8]) -> bool {

pub struct ExitToNear; //TransferEthToNear

impl ExitToNear {
/// Exit to NEAR precompile address
///
/// Address: `0xe9217bc70b7ed1f598ddd3199e80b093fa71124f`
/// This address is computed as: `&keccak("exitToNear")[12..]`
pub(super) const ADDRESS: [u8; 20] =
super::make_address(0xe9217bc7, 0x0b7ed1f598ddd3199e80b093fa71124f);
}

impl Precompile for ExitToNear {
fn required_gas(_input: &[u8]) -> Result<u64, ExitError> {
Ok(costs::EXIT_TO_NEAR_GAS)
Expand Down Expand Up @@ -155,6 +164,15 @@ impl Precompile for ExitToNear {

pub struct ExitToEthereum;

impl ExitToEthereum {
/// Exit to Ethereum precompile address
///
/// Address: `0xb0bd02f6a392af548bdf1cfaee5dfa0eefcc8eab`
/// This address is computed as: `&keccak("exitToEthereum")[12..]`
pub(super) const ADDRESS: [u8; 20] =
super::make_address(0xb0bd02f6, 0xa392af548bdf1cfaee5dfa0eefcc8eab);
}

impl Precompile for ExitToEthereum {
fn required_gas(_input: &[u8]) -> Result<u64, ExitError> {
Ok(costs::EXIT_TO_ETHEREUM_GAS)
Expand Down Expand Up @@ -245,19 +263,18 @@ impl Precompile for ExitToEthereum {

#[cfg(test)]
mod tests {
use super::*;
use crate::precompiles::{EXIT_TO_ETHEREUM_ID, EXIT_TO_NEAR_ID};
use super::{ExitToEthereum, ExitToNear};
use crate::types::near_account_to_evm_address;

#[test]
fn test_precompile_id() {
assert_eq!(
EXIT_TO_ETHEREUM_ID,
near_account_to_evm_address("exitToEthereum".as_bytes()).to_low_u64_be()
ExitToEthereum::ADDRESS,
near_account_to_evm_address("exitToEthereum".as_bytes()).0
);
assert_eq!(
EXIT_TO_NEAR_ID,
near_account_to_evm_address("exitToNear".as_bytes()).to_low_u64_be()
ExitToNear::ADDRESS,
near_account_to_evm_address("exitToNear".as_bytes()).0
);
}
}
4 changes: 4 additions & 0 deletions src/precompiles/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ pub(crate) fn ecrecover(hash: H256, signature: &[u8]) -> Result<Address, ExitErr

pub(super) struct ECRecover;

impl ECRecover {
pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 1);
}

impl Precompile for ECRecover {
fn required_gas(_input: &[u8]) -> Result<u64, ExitError> {
Ok(costs::ECRECOVER_BASE)
Expand Down