Skip to content

Commit

Permalink
Dispatch precompiles on the full address. (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
birchmd authored May 28, 2021
1 parent 060f310 commit 046a560
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 59 deletions.
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],
]
}

#[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

0 comments on commit 046a560

Please sign in to comment.