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(xcc): User payment using wNEAR ERC-20 #572

Merged
merged 8 commits into from
Aug 16, 2022
169 changes: 106 additions & 63 deletions engine-precompiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ use aurora_engine_sdk::env::Env;
use aurora_engine_sdk::io::IO;
use aurora_engine_types::{account_id::AccountId, types::Address, vec, BTreeMap, Box};
use evm::backend::Log;
use evm::executor::{self, stack::PrecompileHandle};
use evm::executor::{
self,
stack::{PrecompileFailure, PrecompileHandle},
};
use evm::{Context, ExitError, ExitSucceed};
use xcc::cross_contract_call;

#[derive(Debug, Default)]
pub struct PrecompileOutput {
Expand Down Expand Up @@ -73,6 +77,13 @@ pub trait Precompile {
) -> EvmPrecompileResult;
}
birchmd marked this conversation as resolved.
Show resolved Hide resolved

pub trait HandleBasedPrecompile {
fn run_with_handle(
&self,
handle: &mut impl PrecompileHandle,
) -> Result<PrecompileOutput, PrecompileFailure>;
}

/// Hard fork marker.
pub trait HardFork {}

Expand All @@ -97,49 +108,64 @@ impl HardFork for Istanbul {}
impl HardFork for Berlin {}

pub struct Precompiles<'a, I, E> {
pub generic_precompiles: prelude::BTreeMap<Address, Box<dyn Precompile>>,
// Cannot be part of the generic precompiles because the `dyn` type-erasure messes with
// with the lifetime requirements on the type parameter `I`.
pub near_exit: ExitToNear<I>,
pub ethereum_exit: ExitToEthereum<I>,
pub cross_contract_call: CrossContractCall<I>,
pub predecessor_account_id: PredecessorAccount<'a, E>,
pub prepaid_gas: PrepaidGas<'a, E>,
pub all_precompiles: prelude::BTreeMap<Address, AllPrecompiles<'a, I, E>>,
}

impl<'a, I: IO + Copy, E: Env> executor::stack::PrecompileSet for Precompiles<'a, I, E> {
fn execute(
&self,
handle: &mut impl PrecompileHandle,
) -> Option<Result<executor::stack::PrecompileOutput, executor::stack::PrecompileFailure>> {
) -> Option<Result<executor::stack::PrecompileOutput, PrecompileFailure>> {
let address = handle.code_address();

self.precompile_action(Address::new(address), |p| {
let input = handle.input();
let gas_limit = handle.gas_limit();
let context = handle.context();
let is_static = handle.is_static();

match p.run(input, gas_limit.map(EthGas::new), context, is_static) {
Ok(output) => {
handle.record_cost(output.cost.as_u64())?;
for log in output.logs {
handle.log(log.address, log.topics, log.data)?;
}
Ok(executor::stack::PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: output.output,
})
}
Err(exit_status) => Err(executor::stack::PrecompileFailure::Error { exit_status }),
}
})
let result = match self.all_precompiles.get(&Address::new(address))? {
AllPrecompiles::ExitToNear(p) => process_precompile(p, handle),
AllPrecompiles::ExitToEthereum(p) => process_precompile(p, handle),
AllPrecompiles::PredecessorAccount(p) => process_precompile(p, handle),
AllPrecompiles::PrepaidGas(p) => process_precompile(p, handle),
AllPrecompiles::CrossContractCall(p) => process_handle_based_precompile(p, handle),
AllPrecompiles::Generic(p) => process_precompile(p.as_ref(), handle),
};
Some(result.and_then(|output| post_process(output, handle)))
}

fn is_precompile(&self, address: prelude::H160) -> bool {
self.precompile_action(Address::new(address), |_| true)
.unwrap_or(false)
self.all_precompiles.contains_key(&Address::new(address))
}
}

fn process_precompile(
p: &dyn Precompile,
handle: &mut impl PrecompileHandle,
) -> Result<PrecompileOutput, PrecompileFailure> {
let input = handle.input();
let gas_limit = handle.gas_limit();
let context = handle.context();
let is_static = handle.is_static();

p.run(input, gas_limit.map(EthGas::new), context, is_static)
.map_err(|exit_status| PrecompileFailure::Error { exit_status })
}

fn process_handle_based_precompile(
p: &impl HandleBasedPrecompile,
handle: &mut impl PrecompileHandle,
) -> Result<PrecompileOutput, PrecompileFailure> {
p.run_with_handle(handle)
}

fn post_process(
output: PrecompileOutput,
handle: &mut impl PrecompileHandle,
) -> Result<executor::stack::PrecompileOutput, PrecompileFailure> {
handle.record_cost(output.cost.as_u64())?;
for log in output.logs {
handle.log(log.address, log.topics, log.data)?;
}
Ok(executor::stack::PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: output.output,
})
}

pub struct PrecompileConstructorContext<'a, I, E> {
Expand All @@ -166,7 +192,11 @@ impl<'a, I: IO + Copy, E: Env> Precompiles<'a, I, E> {
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id.clone())),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();
let map = addresses
.into_iter()
.zip(fun)
.map(|(a, f)| (a, AllPrecompiles::Generic(f)))
.collect();
Self::with_generic_precompiles(map, ctx)
}

Expand Down Expand Up @@ -196,7 +226,11 @@ impl<'a, I: IO + Copy, E: Env> Precompiles<'a, I, E> {
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id.clone())),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();
let map = addresses
.into_iter()
.zip(fun)
.map(|(a, f)| (a, AllPrecompiles::Generic(f)))
.collect();

Self::with_generic_precompiles(map, ctx)
}
Expand Down Expand Up @@ -228,7 +262,11 @@ impl<'a, I: IO + Copy, E: Env> Precompiles<'a, I, E> {
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id.clone())),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();
let map = addresses
.into_iter()
.zip(fun)
.map(|(a, f)| (a, AllPrecompiles::Generic(f)))
.collect();

Self::with_generic_precompiles(map, ctx)
}
Expand Down Expand Up @@ -260,7 +298,11 @@ impl<'a, I: IO + Copy, E: Env> Precompiles<'a, I, E> {
Box::new(RandomSeed::new(ctx.random_seed)),
Box::new(CurrentAccount::new(ctx.current_account_id.clone())),
];
let map: BTreeMap<Address, Box<dyn Precompile>> = addresses.into_iter().zip(fun).collect();
let map = addresses
.into_iter()
.zip(fun)
.map(|(a, f)| (a, AllPrecompiles::Generic(f)))
.collect();

Self::with_generic_precompiles(map, ctx)
}
Expand All @@ -271,7 +313,7 @@ impl<'a, I: IO + Copy, E: Env> Precompiles<'a, I, E> {
}

fn with_generic_precompiles(
generic_precompiles: BTreeMap<Address, Box<dyn Precompile>>,
mut generic_precompiles: BTreeMap<Address, AllPrecompiles<'a, I, E>>,
ctx: PrecompileConstructorContext<'a, I, E>,
) -> Self {
let near_exit = ExitToNear::new(ctx.current_account_id.clone(), ctx.io);
Expand All @@ -280,36 +322,37 @@ impl<'a, I: IO + Copy, E: Env> Precompiles<'a, I, E> {
let predecessor_account_id = PredecessorAccount::new(ctx.env);
let prepaid_gas = PrepaidGas::new(ctx.env);

generic_precompiles.insert(exit_to_near::ADDRESS, AllPrecompiles::ExitToNear(near_exit));
generic_precompiles.insert(
exit_to_ethereum::ADDRESS,
AllPrecompiles::ExitToEthereum(ethereum_exit),
);
generic_precompiles.insert(
cross_contract_call::ADDRESS,
AllPrecompiles::CrossContractCall(cross_contract_call),
);
generic_precompiles.insert(
predecessor_account::ADDRESS,
AllPrecompiles::PredecessorAccount(predecessor_account_id),
);
generic_precompiles.insert(
prepaid_gas::ADDRESS,
AllPrecompiles::PrepaidGas(prepaid_gas),
);

Self {
generic_precompiles,
near_exit,
ethereum_exit,
cross_contract_call,
predecessor_account_id,
prepaid_gas,
all_precompiles: generic_precompiles,
}
}
}

fn precompile_action<U, F: FnOnce(&dyn Precompile) -> U>(
&self,
address: Address,
f: F,
) -> Option<U> {
if address == exit_to_near::ADDRESS {
return Some(f(&self.near_exit));
} else if address == exit_to_ethereum::ADDRESS {
return Some(f(&self.ethereum_exit));
} else if address == xcc::cross_contract_call::ADDRESS {
return Some(f(&self.cross_contract_call));
} else if address == predecessor_account::ADDRESS {
return Some(f(&self.predecessor_account_id));
} else if address == prepaid_gas::ADDRESS {
return Some(f(&self.prepaid_gas));
}
self.generic_precompiles
.get(&address)
.map(|p| f(p.as_ref()))
}
pub enum AllPrecompiles<'a, I, E> {
ExitToNear(ExitToNear<I>),
ExitToEthereum(ExitToEthereum<I>),
CrossContractCall(CrossContractCall<I>),
PredecessorAccount(PredecessorAccount<'a, E>),
PrepaidGas(PrepaidGas<'a, E>),
Generic(Box<dyn Precompile>),
}

/// fn for making an address by concatenating the bytes from two given numbers,
Expand Down
6 changes: 2 additions & 4 deletions engine-precompiles/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@ mod costs {
// TODO(#483): Determine the correct amount of gas
pub(super) const EXIT_TO_ETHEREUM_GAS: EthGas = EthGas::new(0);

// TODO(#332): Determine the correct amount of gas
pub(super) const FT_TRANSFER_GAS: NearGas = NearGas::new(100_000_000_000_000);
pub(super) const FT_TRANSFER_GAS: NearGas = NearGas::new(10_000_000_000_000);

// TODO(#332): Determine the correct amount of gas
#[cfg(feature = "error_refund")]
pub(super) const REFUND_ON_ERROR_GAS: NearGas = NearGas::new(60_000_000_000_000);
pub(super) const REFUND_ON_ERROR_GAS: NearGas = NearGas::new(5_000_000_000_000);

// TODO(#332): Determine the correct amount of gas
pub(super) const WITHDRAWAL_GAS: NearGas = NearGas::new(100_000_000_000_000);
Expand Down
Loading