Skip to content

Commit

Permalink
Feat(Engine): Precompiles for predecessor_account_id and current_acco…
Browse files Browse the repository at this point in the history
…unt_id (#462)
  • Loading branch information
birchmd committed Mar 10, 2022
1 parent 3ea60bb commit ff1e9d7
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 5 deletions.
124 changes: 124 additions & 0 deletions engine-precompiles/src/account_ids.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use super::{EvmPrecompileResult, Precompile};
use crate::prelude::types::{Address, EthGas};
use crate::PrecompileOutput;
use aurora_engine_types::account_id::AccountId;
use evm::{Context, ExitError};

mod costs {
use crate::prelude::types::EthGas;

// TODO(#51): Determine the correct amount of gas
pub(super) const PREDECESSOR_ACCOUNT_GAS: EthGas = EthGas::new(0);
// TODO(#51): Determine the correct amount of gas
pub(super) const CURRENT_ACCOUNT_GAS: EthGas = EthGas::new(0);
}

pub struct PredecessorAccount {
predecessor_account_id: AccountId,
}

impl PredecessorAccount {
/// predecessor_account_id precompile address
///
/// Address: `0x723ffbaba940e75e7bf5f6d61dcbf8d9a4de0fd7`
/// This address is computed as: `&keccak("predecessorAccountId")[12..]`
pub const ADDRESS: Address =
super::make_address(0x723ffbab, 0xa940e75e7bf5f6d61dcbf8d9a4de0fd7);

pub fn new(predecessor_account_id: AccountId) -> Self {
Self {
predecessor_account_id,
}
}
}

impl Precompile for PredecessorAccount {
fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
Ok(costs::PREDECESSOR_ACCOUNT_GAS)
}

fn run(
&self,
input: &[u8],
target_gas: Option<EthGas>,
_context: &Context,
_is_static: bool,
) -> EvmPrecompileResult {
let cost = Self::required_gas(input)?;
if let Some(target_gas) = target_gas {
if cost > target_gas {
return Err(ExitError::OutOfGas);
}
}

Ok(
PrecompileOutput::without_logs(cost, self.predecessor_account_id.as_bytes().to_vec())
.into(),
)
}
}

pub struct CurrentAccount {
current_account_id: AccountId,
}

impl CurrentAccount {
/// current_account_id precompile address
///
/// Address: `0xfefae79e4180eb0284f261205e3f8cea737aff56`
/// This address is computed as: `&keccak("currentAccountId")[12..]`
pub const ADDRESS: Address =
super::make_address(0xfefae79e, 0x4180eb0284f261205e3f8cea737aff56);

pub fn new(current_account_id: AccountId) -> Self {
Self { current_account_id }
}
}

impl Precompile for CurrentAccount {
fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
Ok(costs::PREDECESSOR_ACCOUNT_GAS)
}

fn run(
&self,
input: &[u8],
target_gas: Option<EthGas>,
_context: &Context,
_is_static: bool,
) -> EvmPrecompileResult {
let cost = Self::required_gas(input)?;
if let Some(target_gas) = target_gas {
if cost > target_gas {
return Err(ExitError::OutOfGas);
}
}

Ok(
PrecompileOutput::without_logs(cost, self.current_account_id.as_bytes().to_vec())
.into(),
)
}
}

#[cfg(test)]
mod tests {
use crate::account_ids::{CurrentAccount, PredecessorAccount};
use crate::prelude::sdk::types::near_account_to_evm_address;

#[test]
fn test_predecessor_account_precompile_id() {
assert_eq!(
PredecessorAccount::ADDRESS,
near_account_to_evm_address("predecessorAccountId".as_bytes())
);
}

#[test]
fn test_curent_account_precompile_id() {
assert_eq!(
CurrentAccount::ADDRESS,
near_account_to_evm_address("currentAccountId".as_bytes())
);
}
}
27 changes: 23 additions & 4 deletions engine-precompiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))]
#![cfg_attr(feature = "log", feature(panic_info_message))]

pub mod account_ids;
pub mod blake2;
pub mod bn128;
pub mod hash;
Expand All @@ -15,6 +16,7 @@ pub mod secp256k1;
#[cfg(test)]
mod utils;

use crate::account_ids::{CurrentAccount, PredecessorAccount};
use crate::blake2::Blake2F;
use crate::bn128::{Bn128Add, Bn128Mul, Bn128Pair};
use crate::hash::{RIPEMD160, SHA256};
Expand Down Expand Up @@ -125,6 +127,7 @@ impl executor::stack::PrecompileSet for Precompiles {
pub struct PrecompileConstructorContext {
pub current_account_id: AccountId,
pub random_seed: H256,
pub predecessor_account_id: AccountId,
}

impl Precompiles {
Expand All @@ -137,14 +140,18 @@ impl Precompiles {
ExitToNear::ADDRESS,
ExitToEthereum::ADDRESS,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
PredecessorAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Box::new(SHA256),
Box::new(RIPEMD160),
Box::new(ExitToNear::new(ctx.current_account_id.clone())),
Box::new(ExitToEthereum::new(ctx.current_account_id)),
Box::new(ExitToEthereum::new(ctx.current_account_id.clone())),
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id)),
Box::new(PredecessorAccount::new(ctx.predecessor_account_id)),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();

Expand All @@ -165,6 +172,8 @@ impl Precompiles {
ExitToNear::ADDRESS,
ExitToEthereum::ADDRESS,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
PredecessorAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Expand All @@ -176,8 +185,10 @@ impl Precompiles {
Box::new(Bn128Mul::<Byzantium>::new()),
Box::new(Bn128Pair::<Byzantium>::new()),
Box::new(ExitToNear::new(ctx.current_account_id.clone())),
Box::new(ExitToEthereum::new(ctx.current_account_id)),
Box::new(ExitToEthereum::new(ctx.current_account_id.clone())),
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id)),
Box::new(PredecessorAccount::new(ctx.predecessor_account_id)),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();

Expand All @@ -198,6 +209,8 @@ impl Precompiles {
ExitToNear::ADDRESS,
ExitToEthereum::ADDRESS,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
PredecessorAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Expand All @@ -210,8 +223,10 @@ impl Precompiles {
Box::new(Bn128Pair::<Istanbul>::new()),
Box::new(Blake2F),
Box::new(ExitToNear::new(ctx.current_account_id.clone())),
Box::new(ExitToEthereum::new(ctx.current_account_id)),
Box::new(ExitToEthereum::new(ctx.current_account_id.clone())),
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id)),
Box::new(PredecessorAccount::new(ctx.predecessor_account_id)),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();

Expand All @@ -232,6 +247,8 @@ impl Precompiles {
ExitToNear::ADDRESS,
ExitToEthereum::ADDRESS,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
PredecessorAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Expand All @@ -244,8 +261,10 @@ impl Precompiles {
Box::new(Bn128Pair::<Istanbul>::new()),
Box::new(Blake2F),
Box::new(ExitToNear::new(ctx.current_account_id.clone())),
Box::new(ExitToEthereum::new(ctx.current_account_id)),
Box::new(ExitToEthereum::new(ctx.current_account_id.clone())),
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id)),
Box::new(PredecessorAccount::new(ctx.predecessor_account_id)),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();

Expand Down
69 changes: 69 additions & 0 deletions engine-tests/src/tests/account_id_precompiles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use crate::test_utils::{self, standalone};
use aurora_engine::parameters::SubmitResult;

#[test]
fn test_account_id_precompiles() {
let mut signer = test_utils::Signer::random();
let mut runner = test_utils::deploy_evm();
let mut standalone = standalone::StandaloneRunner::default();

standalone.init_evm();
runner.standalone_runner = Some(standalone);

let constructor = test_utils::solidity::ContractConstructor::compile_from_source(
"src/tests/res",
"target/solidity_build",
"AccountIds.sol",
"AccountIds",
);

// deploy contract
let nonce = signer.use_nonce();
let contract = runner.deploy_contract(
&signer.secret_key,
|c| c.deploy_without_constructor(nonce.into()),
constructor,
);

// check current_account_id is correct
let result = runner
.submit_with_signer(&mut signer, |nonce| {
contract.call_method_without_args("currentAccountId", nonce)
})
.unwrap();
assert_eq!(unwrap_ethabi_string(&result), "aurora");

// check predecessor_account_id is correct
let result = runner
.submit_with_signer(&mut signer, |nonce| {
contract.call_method_without_args("predecessorAccountId", nonce)
})
.unwrap();
assert_eq!(unwrap_ethabi_string(&result), "some-account.near");

// double check the case where account_id is the full 64 bytes
let account_id = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789";
assert_eq!(account_id.len(), 64);
runner.standalone_runner.as_mut().unwrap().env.block_height += 1000;
runner
.standalone_runner
.as_mut()
.unwrap()
.env
.current_account_id = account_id.parse().unwrap();
let nonce = signer.use_nonce();
let tx = contract.call_method_without_args("currentAccountId", nonce.into());
let result = runner
.standalone_runner
.as_mut()
.unwrap()
.submit_transaction(&signer.secret_key, tx)
.unwrap();
assert_eq!(unwrap_ethabi_string(&result), account_id);
}

fn unwrap_ethabi_string(result: &SubmitResult) -> String {
let bytes = test_utils::unwrap_success_slice(result);
let mut tokens = ethabi::decode(&[ethabi::ParamType::String], &bytes).unwrap();
tokens.pop().unwrap().into_string().unwrap()
}
1 change: 1 addition & 0 deletions engine-tests/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod access_lists;
mod account_id_precompiles;
mod contract_call;
mod eip1559;
mod erc20;
Expand Down
45 changes: 45 additions & 0 deletions engine-tests/src/tests/res/AccountIds.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

contract AccountIds {
function currentAccountId() public returns (string memory) {
// Near accounts are at most 64 1-byte characters (see https://docs.near.org/docs/concepts/account#account-id-rules)
bytes32[2] memory value;

assembly {
let ret := call(gas(), 0xfefae79e4180eb0284f261205e3f8cea737aff56, 0, 0, 0, value, 64)
}

return bytes64ToString(value);
}

function predecessorAccountId() public returns (string memory) {
// Near accounts are at most 64 1-byte characters (see https://docs.near.org/docs/concepts/account#account-id-rules)
bytes32[2] memory value;

assembly {
let ret := call(gas(), 0x723ffbaba940e75e7bf5f6d61dcbf8d9a4de0fd7, 0, 0, 0, value, 64)
}

return bytes64ToString(value);
}

function bytes64ToString(bytes32[2] memory value) private pure returns (string memory) {
uint8 result_len = 0;
while((result_len < 32 && value[0][result_len] != 0) || (result_len >= 32 && result_len < 64 && value[1][result_len - 32] != 0)) {
result_len++;
}
bytes memory result = new bytes(result_len);
uint8 i = 0;
for (i = 0; i < 32 && value[0][i] != 0; i++) {
result[i] = value[0][i];
}
if (result_len > 32) {
for (i = 0; i < 32 && value[1][i] != 0; i++) {
result[i + 32] = value[1][i];
}
}

return string(result);
}
}
11 changes: 10 additions & 1 deletion engine/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,17 @@ struct StackExecutorParams {
}

impl StackExecutorParams {
fn new(gas_limit: u64, current_account_id: AccountId, random_seed: H256) -> Self {
fn new(
gas_limit: u64,
current_account_id: AccountId,
predecessor_account_id: AccountId,
random_seed: H256,
) -> Self {
Self {
precompiles: Precompiles::new_london(PrecompileConstructorContext {
current_account_id,
random_seed,
predecessor_account_id,
}),
gas_limit,
}
Expand Down Expand Up @@ -510,6 +516,7 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> {
let executor_params = StackExecutorParams::new(
gas_limit,
self.current_account_id.clone(),
self.env.predecessor_account_id(),
self.env.random_seed(),
);
let mut executor = executor_params.make_executor(self);
Expand Down Expand Up @@ -594,6 +601,7 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> {
let executor_params = StackExecutorParams::new(
gas_limit,
self.current_account_id.clone(),
self.env.predecessor_account_id(),
self.env.random_seed(),
);
let mut executor = executor_params.make_executor(self);
Expand Down Expand Up @@ -643,6 +651,7 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> {
let executor_params = StackExecutorParams::new(
gas_limit,
self.current_account_id.clone(),
self.env.predecessor_account_id(),
self.env.random_seed(),
);
let mut executor = executor_params.make_executor(self);
Expand Down

0 comments on commit ff1e9d7

Please sign in to comment.