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 2 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
17 changes: 9 additions & 8 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 @@ -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 @@ -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
157 changes: 157 additions & 0 deletions engine-precompiles/src/set_gas_token.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
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};
use std::borrow::Cow;

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::set_gas_token::consts;
use aurora_engine_types::types::Address;
use aurora_engine_types::H256;
use evm::backend::Log;

// TODO
pub(crate) 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 data = ethabi::encode(&[ethabi::Token::Address(self.gas_token.raw())]);
let sender_address = {
let mut buf = [0u8; 32];
buf[12..].copy_from_slice(self.sender.as_bytes());
H256(buf)
};
let topics = vec![SET_GAS_TOKEN_SIGNATURE, sender_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 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);
}
}
45 changes: 33 additions & 12 deletions engine-types/src/types/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use borsh::{BorshDeserialize, BorshSerialize};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

const ADDRESS_HEX_LENGTH: usize = 40;
const ADDRESS_BYTE_LENGTH: usize = 20;

/// Base Eth Address type
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand All @@ -26,10 +29,10 @@ impl Address {
}

pub fn decode(address: &str) -> Result<Address, error::AddressError> {
if address.len() != 40 {
if address.len() != ADDRESS_HEX_LENGTH {
return Err(error::AddressError::IncorrectLength);
}
let mut result = [0u8; 20];
let mut result = [0u8; ADDRESS_BYTE_LENGTH];
hex::decode_to_slice(address, &mut result)
.map_err(|_| error::AddressError::FailedDecodeHex)?;
Ok(Address::new(H160(result)))
Expand All @@ -40,18 +43,25 @@ impl Address {
}

pub fn try_from_slice(raw_addr: &[u8]) -> Result<Self, error::AddressError> {
if raw_addr.len() != 20 {
return Err(error::AddressError::IncorrectLength);
use core::cmp::Ordering;
match raw_addr.len().cmp(&ADDRESS_BYTE_LENGTH) {
Ordering::Greater => Err(error::AddressError::IncorrectLength),
Ordering::Less => {
let mut buf = [0u8; ADDRESS_BYTE_LENGTH];
let pos = ADDRESS_BYTE_LENGTH - raw_addr.len();
buf[pos..].copy_from_slice(raw_addr);
Ok(Self::new(H160::from_slice(&buf)))
}
Ordering::Equal => Ok(Self::new(H160::from_slice(raw_addr))),
}
Ok(Self::new(H160::from_slice(raw_addr)))
}

pub const fn from_array(array: [u8; 20]) -> Self {
pub const fn from_array(array: [u8; ADDRESS_BYTE_LENGTH]) -> Self {
Self(H160(array))
}

pub const fn zero() -> Self {
Address::new(H160([0u8; 20]))
Address::new(H160([0u8; ADDRESS_BYTE_LENGTH]))
}
}

Expand All @@ -71,15 +81,15 @@ impl BorshSerialize for Address {

impl BorshDeserialize for Address {
fn deserialize(buf: &mut &[u8]) -> io::Result<Self> {
if buf.len() < 20 {
if buf.len() < ADDRESS_BYTE_LENGTH {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("{}", error::AddressError::IncorrectLength),
));
}
// Guaranty no panics. The length checked early
let address = Self(H160::from_slice(&buf[..20]));
*buf = &buf[20..];
let address = Self(H160::from_slice(&buf[..ADDRESS_BYTE_LENGTH]));
*buf = &buf[ADDRESS_BYTE_LENGTH..];
Ok(address)
}
}
Expand Down Expand Up @@ -139,8 +149,19 @@ mod tests {
}

#[test]
fn test_wrong_address_19() {
let serialized_addr = [0u8; 19];
fn test_address_less_than_20_byte_length() {
let serialized_addr = [0x1u8; 1];
let addr = Address::try_from_slice(&serialized_addr).unwrap();
let expected = Address::try_from_slice(
&hex::decode("0000000000000000000000000000000000000001").unwrap(),
)
.unwrap();
assert_eq!(addr, expected);
}

#[test]
fn test_address_greater_than_20_byte_length() {
let serialized_addr = [0x11u8; 21];
let addr = Address::try_from_slice(&serialized_addr);
let err = addr.unwrap_err();
matches!(err, error::AddressError::IncorrectLength);
Expand Down
2 changes: 2 additions & 0 deletions engine-types/src/types/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ impl Display for EthGas {
}

impl EthGas {
pub const MAX: EthGas = EthGas(u64::MAX);

/// Constructs a new `EthGas` with a given u64 value.
pub const fn new(gas: u64) -> EthGas {
Self(gas)
Expand Down
Loading