Skip to content

Commit

Permalink
Feat(xcc): User payment using wNEAR ERC-20 (#572)
Browse files Browse the repository at this point in the history
  • Loading branch information
birchmd authored Aug 16, 2022
1 parent 2d91055 commit be232fd
Show file tree
Hide file tree
Showing 15 changed files with 927 additions and 217 deletions.
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;
}

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

0 comments on commit be232fd

Please sign in to comment.