-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat(Engine): Precompiles for predecessor_account_id and current_acco…
…unt_id (#462)
- Loading branch information
Showing
6 changed files
with
272 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters