Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
29 changes: 26 additions & 3 deletions src/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,28 @@ mod bn128;
mod hash;
mod identity;
mod modexp;
#[cfg(feature = "contract")]
mod native;
mod secp256k1;

use crate::precompiles::blake2::Blake2F;
use crate::precompiles::bn128::{BN128Add, BN128Mul, BN128Pair};
use crate::precompiles::hash::{RIPEMD160, SHA256};
use crate::precompiles::identity::Identity;
use crate::precompiles::modexp::ModExp;
#[cfg(feature = "contract")]
use crate::precompiles::native::{ExitToEthereum, ExitToNear};
pub(crate) use crate::precompiles::secp256k1::ecrecover;
use crate::precompiles::secp256k1::ECRecover;
use crate::prelude::{Address, Vec};
use evm::{Context, ExitError, ExitSucceed};

#[cfg(feature = "contract")]
const EXIT_TO_NEAR_ID: u64 = 11421322804619973199;

#[cfg(feature = "contract")]
const EXIT_TO_ETHEREUM_ID: u64 = 17176159495920586411;
Comment thread
mfornet marked this conversation as resolved.
Outdated

/// A precompile operation result.
type PrecompileResult = Result<(ExitSucceed, Vec<u8>, u64), ExitError>;

Expand Down Expand Up @@ -78,7 +88,10 @@ pub fn homestead_precompiles(
1 => Some(ECRecover::run(input, target_gas, context)),
2 => Some(SHA256::run(input, target_gas, context)),
3 => Some(RIPEMD160::run(input, target_gas, context)),
// 4 => Some(identity::identity(input, target_gas)),
#[cfg(feature = "contract")]
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
Expand All @@ -105,6 +118,10 @@ pub fn byzantium_precompiles(
6 => Some(BN128Add::<Byzantium>::run(input, target_gas, context)),
7 => Some(BN128Mul::<Byzantium>::run(input, target_gas, context)),
8 => Some(BN128Pair::<Byzantium>::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
Expand Down Expand Up @@ -132,7 +149,10 @@ pub fn istanbul_precompiles(
7 => Some(BN128Mul::<Istanbul>::run(input, target_gas, context)),
8 => Some(BN128Pair::<Istanbul>::run(input, target_gas, context)),
9 => Some(Blake2F::run(input, target_gas, context)),
// Not supported.
#[cfg(feature = "contract")]
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
Expand Down Expand Up @@ -160,7 +180,10 @@ pub fn berlin_precompiles(
7 => Some(BN128Mul::<Istanbul>::run(input, target_gas, context)),
8 => Some(BN128Pair::<Istanbul>::run(input, target_gas, context)),
9 => Some(Blake2F::run(input, target_gas, context)),
// Not supported.
#[cfg(feature = "contract")]
EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)),
#[cfg(feature = "contract")]
EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)),
_ => None,
}
}
187 changes: 187 additions & 0 deletions src/precompiles/native.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
use evm::{Context, ExitError, ExitSucceed};

use super::{Precompile, PrecompileResult};
use crate::prelude::{String, Vec, U256, ToString};
use crate::sdk;
use crate::types::{AccountId};

mod costs {
use crate::types::Gas;

// TODO(#51): Determine the correct amount of gas
pub(super) const EXIT_TO_NEAR_GAS: Gas = 0;

// TODO(#51): Determine the correct amount of gas
pub(super) const EXIT_TO_ETHEREUM_GAS: Gas = 0;

// TODO(#51): Determine the correct amount of gas
pub(super) const FT_TRANSFER_GAS: Gas = 100_000_000_000_000;

// TODO(#51): Determine the correct amount of gas
pub(super) const WITHDRAWAL_GAS: Gas = 100_000_000_000_000;
}

/// Get the current nep141 token associated with the current erc20 token.
/// This will fail is none is associated.
fn get_nep141_from_erc20(_erc20_token: &[u8]) -> AccountId {
// TODO:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where it will TODO? If not planned now we should create an issue.

@mfornet mfornet May 12, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is done in pr #51 , didnt remove for it to compile

"".to_string()
}

pub struct ExitToNear; //TransferEthToNear

impl Precompile for ExitToNear {
fn required_gas(_input: &[u8]) -> Result<u64, ExitError> {
Ok(costs::EXIT_TO_NEAR_GAS)
}

fn run(input: &[u8], target_gas: u64, context: &Context) -> PrecompileResult {
if Self::required_gas(input)? > target_gas {
return Err(ExitError::OutOfGas);
}

let (nep141_address, args) = if context.apparent_value != U256::from(0) {
// ETH transfer
//
// Input slice format:
// recipient_account_id (bytes) - the NEAR recipient account which will receive NEP-141 ETH tokens

(
String::from_utf8(sdk::current_account_id()).unwrap(),
crate::prelude::format!(
r#"{{"receiver_id": "{}", "amount": "{}", "memo": null}}"#,
String::from_utf8(input.to_vec()).unwrap(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add a check to validate the recipient_account_id and fail if any other data is provided (as we've discussed).

context.apparent_value.as_u128()
),
)
} else {
// ERC20 transfer
//
// This precompile branch is expected to be called from the ERC20 burn function\
//
// Input slice format:
// amount (U256 le bytes) - the amount that was burned
// recipient_account_id (bytes) - the NEAR recipient account which will receive NEP-141 tokens

let nep141_address = get_nep141_from_erc20(context.caller.as_bytes());

let mut input_mut = input;
let amount = U256::from_big_endian(&input_mut[..32]).as_u128();
input_mut = &input_mut[32..];
let receiver_account_id: AccountId = String::from_utf8(input_mut.to_vec()).unwrap();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same thing about the receiver_account_id validation

(
nep141_address,
crate::prelude::format!(
r#"{{"receiver_id": "{}", "amount": "{}", "memo": null}}"#,
receiver_account_id,
amount
),
)
};

let promise0 = sdk::promise_create(
nep141_address,
b"ft_transfer",
args.as_bytes(),
1,
costs::FT_TRANSFER_GAS,
);

sdk::promise_return(promise0);

Ok((ExitSucceed::Returned, Vec::new(), 0))
}
}

pub struct ExitToEthereum;

impl Precompile for ExitToEthereum {
fn required_gas(_input: &[u8]) -> Result<u64, ExitError> {
Ok(costs::EXIT_TO_ETHEREUM_GAS)
}

fn run(input: &[u8], target_gas: u64, context: &Context) -> PrecompileResult {
if Self::required_gas(input)? > target_gas {
return Err(ExitError::OutOfGas);
}

let (nep141_address, serialized_args) = if context.apparent_value != U256::from(0) {
// ETH transfer
//
// Input slice format:
// eth_recipient (20 bytes) - the address of recipient which will receive ETH on Ethereum

let eth_recipient: String = hex::encode(input);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, check the input is exactly 20 bytes long.


(
String::from_utf8(sdk::current_account_id()).unwrap(),
crate::prelude::format!(
r#"{{"amount": "{}", "recipient": "{}"}}"#,
context.apparent_value.as_u128(),
eth_recipient
),
)
} else {
// ERC-20 transfer
//
// This precompile branch is expected to be called from the ERC20 withdraw function
// (or burn function with some flag provided that this is expected to be withdrawn)
//
// Input slice format:
// amount (U256 le bytes) - the amount that was burned
// eth_recipient (20 bytes) - the address of recipient which will receive ETH on Ethereum

let nep141_address = get_nep141_from_erc20(context.caller.as_bytes());

let mut input_mut = input;

let amount = U256::from_big_endian(&input_mut[..32]).as_u128();
input_mut = &input_mut[32..];

assert_eq!(input_mut.len(), 20);

// Parse ethereum address in hex
let eth_recipient: String = hex::encode(input_mut.to_vec());

(
nep141_address,
crate::prelude::format!(
r#"{{"amount": "{}", "recipient": "{}"}}"#,
amount,
eth_recipient
),
)
};

let promise0 = sdk::promise_create(
nep141_address,
b"withdraw",
serialized_args.as_bytes(),
1,
costs::WITHDRAWAL_GAS,
);

sdk::promise_return(promise0);

Ok((ExitSucceed::Returned, Vec::new(), 0))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::precompiles::{EXIT_TO_ETHEREUM_ID, EXIT_TO_NEAR_ID};
use crate::types::near_account_to_evm_address;

#[test]
fn test_precompile_id() {
assert_eq!(
EXIT_TO_ETHEREUM_ID,
near_account_to_evm_address("exitToEthereum".as_bytes()).to_low_u64_be()
);
assert_eq!(
EXIT_TO_NEAR_ID,
near_account_to_evm_address("exitToNear".as_bytes()).to_low_u64_be()
);
}
}
5 changes: 3 additions & 2 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub use alloc::{
borrow::{Cow, Cow::*},
boxed::Box,
collections::BTreeMap as HashMap,
fmt,
fmt, format,
string::String,
string::ToString,
vec,
Expand All @@ -15,7 +15,8 @@ pub use core::{convert::TryInto, marker::PhantomData, mem};
#[cfg(feature = "std")]
pub use std::{
borrow::Cow::Borrowed, borrow::ToOwned, boxed::Box, collections::HashMap, convert::TryInto,
error::Error, fmt, marker::PhantomData, mem, string::String, string::ToString, vec, vec::Vec,
error::Error, fmt, format, marker::PhantomData, mem, string::String, string::ToString, vec,
vec::Vec,
};

pub use primitive_types::{H160, H256, U256};
Expand Down
24 changes: 22 additions & 2 deletions src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ mod exports {
// # Context API #
// ###############
pub(crate) fn current_account_id(register_id: u64);
fn signer_account_id(register_id: u64);
fn signer_account_pk(register_id: u64);
pub(crate) fn signer_account_id(register_id: u64);
pub(crate) fn signer_account_pk(register_id: u64);
pub(crate) fn predecessor_account_id(register_id: u64);
pub(crate) fn input(register_id: u64);
// TODO #1903 fn block_height() -> u64;
Expand Down Expand Up @@ -278,6 +278,26 @@ pub fn predecessor_account_id() -> Vec<u8> {
}
}

#[allow(dead_code)]
pub fn signer_account_id() -> Vec<u8> {
unsafe {
exports::signer_account_id(1);
let bytes: Vec<u8> = vec![0u8; exports::register_len(1) as usize];
exports::read_register(1, bytes.as_ptr() as *const u64 as u64);
bytes
}
}

#[allow(dead_code)]
pub fn signer_account_pk() -> Vec<u8> {
unsafe {
exports::signer_account_pk(1);
let bytes: Vec<u8> = vec![0u8; exports::register_len(1) as usize];
exports::read_register(1, bytes.as_ptr() as *const u64 as u64);
bytes
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need these in the current PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We dont!


/// Calls environment sha256 on given input.
#[allow(dead_code)]
pub fn sha256(input: &[u8]) -> H256 {
Expand Down
6 changes: 6 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::prelude::{Address, String, Vec, H256, U256};
use borsh::{BorshDeserialize, BorshSerialize};

#[cfg(not(feature = "contract"))]
use sha3::{Digest, Keccak256};
Expand All @@ -10,6 +11,11 @@ pub type AccountId = String;
pub type RawAddress = [u8; 20];
pub type RawU256 = [u8; 32]; // Little-endian large integer type.
pub type RawH256 = [u8; 32]; // Unformatted binary data of fixed length.
pub type Gas = u64;
pub type Balance = u128;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just notice that we have the same type et eth-connector

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have it here since they are common to both

#[derive(BorshSerialize, BorshDeserialize)]
pub struct U128(pub u128);

pub const STORAGE_PRICE_PER_BYTE: u128 = 100_000_000_000_000_000_000; // 1e20yN, 0.0001N

Expand Down