Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(engine): erc20 gas token #662

Draft
wants to merge 17 commits into
base: develop
Choose a base branch
from
Draft
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
23 changes: 12 additions & 11 deletions engine-precompiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod prepaid_gas;
pub mod promise_result;
pub mod random;
pub mod secp256k1;
pub mod set_gas_token;
mod utils;
pub mod xcc;

Expand Down Expand Up @@ -111,8 +112,8 @@ impl HardFork for Istanbul {}
impl HardFork for Berlin {}

pub struct Precompiles<'a, I, E, H> {
pub all_precompiles: prelude::BTreeMap<Address, AllPrecompiles<'a, I, E, H>>,
pub paused_precompiles: prelude::BTreeSet<Address>,
pub all_precompiles: BTreeMap<Address, AllPrecompiles<'a, I, E, H>>,
pub paused_precompiles: BTreeSet<Address>,
}

impl<'a, I, E, H> Precompiles<'a, I, E, H> {
Expand Down Expand Up @@ -149,7 +150,7 @@ impl<'a, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> executor::stack::Preco
Some(result.and_then(|output| post_process(output, handle)))
}

fn is_precompile(&self, address: prelude::H160) -> bool {
fn is_precompile(&self, address: H160) -> bool {
self.all_precompiles.contains_key(&Address::new(address))
}
}
Expand Down Expand Up @@ -206,7 +207,7 @@ impl<'a, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> Precompiles<'a, I, E,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
let fun: Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Box::new(SHA256),
Box::new(RIPEMD160),
Expand Down Expand Up @@ -235,7 +236,7 @@ impl<'a, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> Precompiles<'a, I, E,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
let fun: Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Box::new(SHA256),
Box::new(RIPEMD160),
Expand Down Expand Up @@ -270,7 +271,7 @@ impl<'a, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> Precompiles<'a, I, E,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
let fun: Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Box::new(SHA256),
Box::new(RIPEMD160),
Expand Down Expand Up @@ -306,7 +307,7 @@ impl<'a, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> Precompiles<'a, I, E,
RandomSeed::ADDRESS,
CurrentAccount::ADDRESS,
];
let fun: prelude::Vec<Box<dyn Precompile>> = vec![
let fun: Vec<Box<dyn Precompile>> = vec![
Box::new(ECRecover),
Box::new(SHA256),
Box::new(RIPEMD160),
Expand Down Expand Up @@ -386,10 +387,10 @@ pub enum AllPrecompiles<'a, I, E, H> {
/// 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.
pub const fn make_address(x: u32, y: u128) -> prelude::types::Address {
pub const fn make_address(x: u32, y: u128) -> Address {
let x_bytes = x.to_be_bytes();
let y_bytes = y.to_be_bytes();
prelude::types::Address::new(H160([
Address::new(H160([
x_bytes[0],
x_bytes[1],
x_bytes[2],
Expand All @@ -413,10 +414,10 @@ pub const fn make_address(x: u32, y: u128) -> prelude::types::Address {
]))
}

const fn make_h256(x: u128, y: u128) -> prelude::H256 {
const fn make_h256(x: u128, y: u128) -> H256 {
let x_bytes = x.to_be_bytes();
let y_bytes = y.to_be_bytes();
prelude::H256([
H256([
x_bytes[0],
x_bytes[1],
x_bytes[2],
Expand Down
175 changes: 175 additions & 0 deletions engine-precompiles/src/set_gas_token.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use crate::prelude::{vec, Cow};
use crate::set_gas_token::events::SetGasTokenLog;
use crate::{EvmPrecompileResult, Precompile, PrecompileOutput};
use aurora_engine_types::types::{Address, EthGas};
use evm::backend::Log;
use evm::{Context, ExitError};

pub use consts::SET_GAS_TOKEN_ADDRESS;

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

// TODO: gas costs, could be calculated returning logs of NEAR gas used prior and after.
// Should check if the gas check adds gas itself as well..?
pub(super) const SET_GAS_TOKEN_GAS: EthGas = EthGas::new(0);
}

pub mod consts {
use aurora_engine_types::types::Address;

/// Change gas token precompile address.
///
/// Address: `0x076dae45c8e16a92258252fe04dedd97f1ea93d6`
///
/// This address is computed as: `keccak("setGasToken")[12..]`
pub const SET_GAS_TOKEN_ADDRESS: Address =
crate::make_address(0x076dae45, 0xc8e16a92258252fe04dedd97f1ea93d6);
}

pub mod events {
use crate::prelude::vec;
use crate::set_gas_token::consts;
use aurora_engine_types::types::Address;
use aurora_engine_types::H256;
use evm::backend::Log;

pub const SET_GAS_TOKEN_SIGNATURE: H256 = crate::make_h256(
0x29d0b6eaa171d0d1607729f506329510,
0x7bc9766ba17d250f129cb5bd06503d13,
);

pub(crate) struct SetGasTokenLog {
pub sender: Address,
pub gas_token: Address,
}

impl SetGasTokenLog {
pub(crate) fn encode(self) -> Log {
let gas_token_address = {
let mut buf = [0u8; 32];
buf[12..].copy_from_slice(self.gas_token.as_bytes());
H256(buf)
};
let data = ethabi::encode(&[ethabi::Token::Address(self.sender.raw())]);
let topics = vec![SET_GAS_TOKEN_SIGNATURE, gas_token_address];

let raw_log = ethabi::RawLog { topics, data };

Log {
address: consts::SET_GAS_TOKEN_ADDRESS.raw(),
topics: raw_log.topics,
data: raw_log.data,
}
}
}

#[cfg(test)]
pub fn set_gas_token_schema() -> ethabi::Event {
ethabi::Event {
name: "SetGasToken".into(),
inputs: vec![
ethabi::EventParam {
name: "sender".into(),
kind: ethabi::ParamType::Address,
indexed: true,
},
ethabi::EventParam {
name: "gas_token".into(),
kind: ethabi::ParamType::Address,
indexed: true,
},
],
anonymous: false,
}
}
}

/// A precompile contract used to set the gas token.
///
/// Takes an input which must be an approved ERC-20 contract, or ETH itself at
/// the address `0x0`.
pub struct SetGasToken;

impl SetGasToken {
pub const ADDRESS: Address = SET_GAS_TOKEN_ADDRESS;
}

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

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

// It's not allowed to call exit precompiles in static mode
if is_static {
return Err(ExitError::Other(Cow::from("ERR_INVALID_IN_STATIC")));
} else if context.address != Self::ADDRESS.raw() {
return Err(ExitError::Other(Cow::from("ERR_INVALID_IN_DELEGATE")));
}

let set_gas_token_log: Log = {
let sender = Address::new(context.caller);
let gas_token = Address::try_from_slice(input)
.map_err(|_e| ExitError::Other(Cow::from("ERR_INVALID_ETH_ADDRESS")))?;
SetGasTokenLog { sender, gas_token }.encode()
};

Ok(PrecompileOutput {
cost: required_gas,
logs: vec![set_gas_token_log],
..Default::default()
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::H160;
use aurora_engine_sdk::types::near_account_to_evm_address;

#[test]
fn test_precompile_id() {
assert_eq!(
SET_GAS_TOKEN_ADDRESS,
near_account_to_evm_address("setGasToken".as_bytes())
);
}

#[test]
fn test_signature() {
let schema = events::set_gas_token_schema();
assert_eq!(schema.signature(), events::SET_GAS_TOKEN_SIGNATURE);
}

#[test]
fn test_run() {
let set_gas_token = SetGasToken;
let user = H160([0x11u8; 20]);
let target_gas = EthGas::new(10_000);
let context = Context {
address: SET_GAS_TOKEN_ADDRESS.raw(),
caller: user,
apparent_value: Default::default(),
};
let input = hex::decode("8BEc47865aDe3B172A928df8f990Bc7f2A3b9f79").unwrap(); // Aurora mainnet contract address
let result = set_gas_token
.run(&input, Some(target_gas), &context, false)
.unwrap();
println!("{result:?}");
}
}
2 changes: 1 addition & 1 deletion engine-sdk/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub trait IO {
Ok(U256::from_big_endian(&result))
}

fn write_borsh<T: BorshSerialize>(
fn write_storage_borsh<T: BorshSerialize>(
&mut self,
key: &[u8],
value: &T,
Expand Down
11 changes: 6 additions & 5 deletions engine-standalone-storage/src/relayer_db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ pub mod error {
mod test {
use super::FallibleIterator;
use crate::sync::types::{TransactionKind, TransactionMessage};
use aurora_engine::state::EngineStateV2;
use aurora_engine::{connector, parameters, state};
use aurora_engine_types::H256;

Expand All @@ -210,12 +211,12 @@ mod test {
fn test_fill_db() {
let mut storage = crate::Storage::open("rocks_tmp/").unwrap();
let mut connection = super::connect_without_tls(&Default::default()).unwrap();
let engine_state = state::EngineState {
let engine_state = state::EngineState::V2(EngineStateV2 {
chain_id: aurora_engine_types::types::u256_to_arr(&1313161555.into()),
owner_id: "aurora".parse().unwrap(),
bridge_prover_id: "prover.bridge.near".parse().unwrap(),
upgrade_delay_blocks: 0,
};
default_gas_token: Default::default(),
});

// Initialize engine and connector states in storage.
// Use explicit scope so borrows against `storage` are dropped before processing DB rows.
Expand All @@ -234,9 +235,9 @@ mod test {
state::set_state(&mut local_io, engine_state.clone()).unwrap();
connector::EthConnectorContract::create_contract(
io,
engine_state.owner_id.clone(),
engine_state.owner_id(),
parameters::InitCallArgs {
prover_account: engine_state.bridge_prover_id.clone(),
prover_account: "prover.bridge.near".parse().unwrap(),
eth_custodian_address: "6bfad42cfc4efc96f529d786d643ff4a8b89fa52"
.to_string(),
metadata: Default::default(),
Expand Down
4 changes: 2 additions & 2 deletions engine-standalone-storage/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,9 @@ pub enum ConsumeMessageOutcome {

#[derive(Debug)]
pub struct TransactionIncludedOutcome {
pub hash: aurora_engine_types::H256,
pub hash: H256,
pub info: TransactionMessage,
pub diff: crate::Diff,
pub diff: Diff,
pub maybe_result: Result<Option<TransactionExecutionResult>, error::Error>,
}

Expand Down
5 changes: 2 additions & 3 deletions engine-tests/src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,8 @@ pub(crate) fn deploy_evm() -> AuroraRunner {
let args = NewCallArgs {
chain_id: crate::prelude::u256_to_arr(&U256::from(runner.chain_id)),
owner_id: str_to_account_id(runner.aurora_account_id.as_str()),
bridge_prover_id: str_to_account_id("bridge_prover.near"),
upgrade_delay_blocks: 1,
default_gas_token: [0u8; 20],
};

let account_id = runner.aurora_account_id.clone();
Expand Down Expand Up @@ -821,8 +821,7 @@ pub(crate) fn as_account_id(account_id: &str) -> near_primitives_core::types::Ac
}

pub(crate) fn str_to_account_id(account_id: &str) -> AccountId {
use aurora_engine_types::str::FromStr;
AccountId::from_str(account_id).unwrap()
AccountId::new(account_id).unwrap()
}

pub fn unwrap_success(result: SubmitResult) -> Vec<u8> {
Expand Down
4 changes: 2 additions & 2 deletions engine-tests/src/test_utils/standalone/mocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub const ETH_CUSTODIAN_ADDRESS: Address =
aurora_engine_precompiles::make_address(0xd045f7e1, 0x9b2488924b97f9c145b5e51d0d895a65);

pub fn compute_block_hash(block_height: u64) -> H256 {
aurora_engine::engine::compute_block_hash([0u8; 32], block_height, b"aurora")
engine::compute_block_hash([0u8; 32], block_height, b"aurora")
}

pub fn insert_block(storage: &mut Storage, block_height: u64) {
Expand Down Expand Up @@ -52,8 +52,8 @@ pub fn init_evm<I: IO + Copy, E: Env>(mut io: I, env: &E, chain_id: u64) {
let new_args = NewCallArgs {
chain_id: aurora_engine_types::types::u256_to_arr(&U256::from(chain_id)),
owner_id: env.current_account_id(),
bridge_prover_id: test_utils::str_to_account_id("bridge_prover.near"),
upgrade_delay_blocks: 1,
default_gas_token: [0u8; 20], // Base coin
};

state::set_state(&mut io, new_args.into()).unwrap();
Expand Down
3 changes: 1 addition & 2 deletions engine-tests/src/tests/eth_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use aurora_engine_types::types::{Fee, NEP141Wei};
use borsh::{BorshDeserialize, BorshSerialize};
use byte_slice_cast::AsByteSlice;
use ethabi::ethereum_types::U256;
use near_sdk::test_utils::accounts;
use near_sdk_sim::transaction::ExecutionStatus;
use near_sdk_sim::{to_yocto, ExecutionResult, UserAccount, DEFAULT_GAS, STORAGE_AMOUNT};
use serde_json::json;
Expand Down Expand Up @@ -71,8 +70,8 @@ fn init_contract(
&NewCallArgs {
chain_id: [0u8; 32],
owner_id: str_to_account_id(master_account.account_id.clone().as_str()),
bridge_prover_id: str_to_account_id(accounts(0).as_str()),
upgrade_delay_blocks: 1,
default_gas_token: [0u8; 20],
}
.try_to_vec()
.unwrap(),
Expand Down
5 changes: 3 additions & 2 deletions engine-tests/src/tests/sanity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,16 @@ fn test_state_format() {
let args = aurora_engine::parameters::NewCallArgs {
chain_id: aurora_engine_types::types::u256_to_arr(&666.into()),
owner_id: "boss".parse().unwrap(),
bridge_prover_id: "prover_mcprovy_face".parse().unwrap(),
upgrade_delay_blocks: 3,
default_gas_token: [255u8; 20],
};
let state: aurora_engine::state::EngineState = args.into();
let expected_hex: String = [
"00", // V2 - "00", V1 - "01",
"000000000000000000000000000000000000000000000000000000000000029a",
"04000000626f7373",
"1300000070726f7665725f6d6370726f76795f66616365",
"0300000000000000",
"01ffffffffffffffffffffffffffffffffffffffff",
]
.concat();
assert_eq!(hex::encode(state.try_to_vec().unwrap()), expected_hex);
Expand Down
Loading