Skip to content

Commit

Permalink
feat(runtime): restrict creation of Ethereum address on NEAR
Browse files Browse the repository at this point in the history
  • Loading branch information
bowenwang1996 committed Jul 27, 2023
1 parent 6524cd2 commit 62367ed
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 15 deletions.
40 changes: 40 additions & 0 deletions core/account-id/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@ impl AccountId {
!self.is_system() && !self.contains('.')
}

/// Returns `true` if the account id can be viewed as an Ethereum address directly
///
/// See [Ethereum account](https://ethereum.org/en/developers/docs/accounts/) for more details.
///
/// ## Examples
/// ```
/// use near_account_id::AccountId;
///
/// let eth_addr: AccountId = "0x06012c8cf97bead5deae237070f9587f8e7a266d".parse().unwrap();
/// assert!(eth_addr.is_ethereum_address());
///
/// let non_eth_addr: AccountId = "0x".parse().unwrap();
/// assert!(!non_eth_addr.is_ethereum_address());
/// ```
pub fn is_ethereum_address(&self) -> bool {
self.starts_with("0x") && self.len() == 42
}

/// Returns `true` if the `AccountId` is a direct sub-account of the provided parent account.
///
/// See [Subaccounts](https://docs.near.org/docs/concepts/account#subaccounts).
Expand Down Expand Up @@ -720,6 +738,28 @@ mod tests {
}
}

#[test]
fn test_is_ethereum_address() {
let valid_ethereum_addresses = &[
"0x06012c8cf97bead5deae237070f9587f8e7a266d",
"0x5e97870f263700f46aa00d967821199b9bc5a120",
"0x0000000000000000000000000000000000000000",
];
for account_id in valid_ethereum_addresses {
assert!(account_id.parse::<AccountId>().unwrap().is_ethereum_address());
}

let non_ethereum_addresses = &[
"alice.near",
"near",
"0x",
"20782e20662e64666420482123494b6b6c677573646b6c66676a646b6c736667",
];
for account_id in non_ethereum_addresses {
assert!(!account_id.parse::<AccountId>().unwrap().is_ethereum_address());
}
}

#[test]
#[cfg(feature = "arbitrary")]
fn test_arbitrary() {
Expand Down
2 changes: 2 additions & 0 deletions core/primitives-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protocol_feature_fix_contract_loading_cost = []
protocol_feature_reject_blocks_with_outdated_protocol_version = []
protocol_feature_simple_nightshade_v2 = []
protocol_feature_block_header_v4 = []
protocol_feature_ethereum_address = []

nightly = [
"nightly_protocol",
Expand All @@ -45,6 +46,7 @@ nightly = [
"protocol_feature_fix_staking_threshold",
"protocol_feature_reject_blocks_with_outdated_protocol_version",
"protocol_feature_simple_nightshade_v2",
"protocol_feature_ethereum_address",
]

nightly_protocol = [
Expand Down
6 changes: 5 additions & 1 deletion core/primitives-core/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ pub enum ProtocolFeature {
SimpleNightshadeV2,
#[cfg(feature = "protocol_feature_block_header_v4")]
BlockHeaderV4,
#[cfg(feature = "protocol_feature_ethereum_address")]
EthereumAddress,
}

impl ProtocolFeature {
Expand Down Expand Up @@ -178,6 +180,8 @@ impl ProtocolFeature {
ProtocolFeature::SimpleNightshadeV2 => 135,
#[cfg(feature = "protocol_feature_block_header_v4")]
ProtocolFeature::BlockHeaderV4 => 138,
#[cfg(feature = "protocol_feature_ethereum_address")]
ProtocolFeature::EthereumAddress => 139,
}
}
}
Expand All @@ -190,7 +194,7 @@ const STABLE_PROTOCOL_VERSION: ProtocolVersion = 62;
/// Largest protocol version supported by the current binary.
pub const PROTOCOL_VERSION: ProtocolVersion = if cfg!(feature = "nightly_protocol") {
// On nightly, pick big enough version to support all features.
138
139
} else {
// Enable all stable features.
STABLE_PROTOCOL_VERSION
Expand Down
24 changes: 19 additions & 5 deletions core/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,27 @@ near-vm-runner.workspace = true
[features]
sandbox = []
dump_errors_schema = ["near-rpc-error-macro/dump_errors_schema"]
protocol_feature_fix_staking_threshold = ["near-primitives-core/protocol_feature_fix_staking_threshold"]
protocol_feature_fix_contract_loading_cost = ["near-primitives-core/protocol_feature_fix_contract_loading_cost"]
protocol_feature_reject_blocks_with_outdated_protocol_version = ["near-primitives-core/protocol_feature_reject_blocks_with_outdated_protocol_version"]
protocol_feature_simple_nightshade_v2 = ["near-primitives-core/protocol_feature_simple_nightshade_v2"]
protocol_feature_block_header_v4 = ["near-primitives-core/protocol_feature_block_header_v4"]
protocol_feature_fix_staking_threshold = [
"near-primitives-core/protocol_feature_fix_staking_threshold",
]
protocol_feature_fix_contract_loading_cost = [
"near-primitives-core/protocol_feature_fix_contract_loading_cost",
]
protocol_feature_reject_blocks_with_outdated_protocol_version = [
"near-primitives-core/protocol_feature_reject_blocks_with_outdated_protocol_version",
]
protocol_feature_simple_nightshade_v2 = [
"near-primitives-core/protocol_feature_simple_nightshade_v2",
]
protocol_feature_block_header_v4 = [
"near-primitives-core/protocol_feature_block_header_v4",
]
protocol_feature_ethereum_address = [
"near-primitives-core/protocol_feature_ethereum_address",
]
nightly = [
"nightly_protocol",
"protocol_feature_ethereum_address",
"protocol_feature_block_header_v4",
"protocol_feature_fix_contract_loading_cost",
"protocol_feature_fix_staking_threshold",
Expand Down
3 changes: 3 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ protocol_feature_reject_blocks_with_outdated_protocol_version = [
protocol_feature_simple_nightshade_v2 = [
"near-primitives/protocol_feature_simple_nightshade_v2",
]
protocol_feature_ethereum_address = [
"nearcore/protocol_feature_ethereum_address",
]

nightly = [
"nightly_protocol",
Expand Down
30 changes: 30 additions & 0 deletions integration-tests/src/tests/standard_cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,36 @@ pub fn test_create_account_failure_already_exists(node: impl Node) {
);
}

pub fn test_create_ethereum_address(node: impl Node) {
let account_id = &node.account_id().unwrap();
let node_user = node.user();
let valid_ethereum_addresses = [
"0x06012c8cf97bead5deae237070f9587f8e7a266d",
"0x5e97870f263700f46aa00d967821199b9bc5a120",
"0x0000000000000000000000000000000000000000",
];
for (_, address) in valid_ethereum_addresses.iter().enumerate() {
let new_account_id = address.parse::<AccountId>().unwrap();
let transaction_result = node_user
.create_account(account_id.clone(), new_account_id.clone(), node.signer().public_key(), 0)
.unwrap();
assert_eq!(
transaction_result.status,
FinalExecutionStatus::Failure(
ActionError {
index: Some(0),
kind: ActionErrorKind::CreateAccountOnlyByRegistrar {
account_id: new_account_id,
registrar_account_id: "registrar".parse().unwrap(),
predecessor_id: account_id.clone()
}
}
.into()
)
);
}
}

pub fn test_swap_key(node: impl Node) {
let account_id = &node.account_id().unwrap();
let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519);
Expand Down
8 changes: 8 additions & 0 deletions integration-tests/src/tests/standard_cases/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,11 @@ fn test_storage_read_write_costs_runtime() {
let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone();
test_storage_read_write_costs(node, runtime_config);
}

#[test]
#[cfg(feature = "protocol_feature_ethereum_address")]
fn test_create_ethereum_address_runtime() {
let node = create_runtime_node();
let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone();
test_create_ethereum_address(node);
}
13 changes: 8 additions & 5 deletions nearcore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,13 @@ harness = false
[features]
default = ["json_rpc", "rosetta_rpc"]

performance_stats = [
"near-performance-metrics/performance_stats",
]
performance_stats = ["near-performance-metrics/performance_stats"]
c_memory_stats = ["near-performance-metrics/c_memory_stats"]
test_features = [
"near-client/test_features",
"near-network/test_features",
"near-store/test_features",
"near-jsonrpc/test_features"
"near-jsonrpc/test_features",
]
expensive_tests = [
"near-client/expensive_tests",
Expand All @@ -116,12 +114,17 @@ protocol_feature_fix_contract_loading_cost = [
"near-vm-runner/protocol_feature_fix_contract_loading_cost",
]
protocol_feature_simple_nightshade_v2 = [
"near-primitives/protocol_feature_simple_nightshade_v2",
"near-primitives/protocol_feature_simple_nightshade_v2",
]
protocol_feature_ethereum_address = [
"near-primitives/protocol_feature_ethereum_address",
"node-runtime/protocol_feature_ethereum_address",
]

serialize_all_state_changes = ["near-store/serialize_all_state_changes"]
nightly = [
"nightly_protocol",
"protocol_feature_ethereum_address",
"protocol_feature_fix_contract_loading_cost",
"protocol_feature_fix_staking_threshold",
"protocol_feature_simple_nightshade_v2",
Expand Down
8 changes: 4 additions & 4 deletions runtime/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ near-store.workspace = true
near-vm-runner.workspace = true

[features]
protocol_feature_ethereum_address = ["near-primitives/protocol_feature_ethereum_address"]
nightly = [
"nightly_protocol",
"near-chain-configs/nightly",
"near-o11y/nightly",
"near-primitives/nightly",
"near-store/nightly",
"near-vm-runner/nightly",
"protocol_feature_ethereum_address",
]
default = []
nightly_protocol = [
Expand All @@ -46,13 +48,11 @@ nightly_protocol = [
"near-primitives/nightly_protocol",
"near-store/nightly_protocol",
"near-vm-runner/nightly_protocol",
"protocol_feature_ethereum_address",
]
no_cpu_compatibility_checks = ["near-vm-runner/no_cpu_compatibility_checks"]

no_cache = [
"near-vm-runner/no_cache",
"near-store/no_cache",
]
no_cache = ["near-vm-runner/no_cache", "near-store/no_cache"]

sandbox = ["near-vm-runner/sandbox"]

Expand Down
42 changes: 42 additions & 0 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ pub(crate) fn action_create_account(
account_id: &AccountId,
predecessor_id: &AccountId,
result: &mut ActionResult,
current_protocol_version: ProtocolVersion,
) {
if account_id.is_top_level() {
if account_id.len() < account_creation_config.min_allowed_top_level_account_length as usize
Expand All @@ -395,6 +396,23 @@ pub(crate) fn action_create_account(
}
.into());
return;
} else if checked_feature!(
"protocol_feature_ethereum_address",
EthereumAddress,
current_protocol_version
) {
if account_id.is_ethereum_address()
&& predecessor_id != &account_creation_config.registrar_account_id
{
// An Ethereum address can only be created by the registrar account
result.result = Err(ActionErrorKind::CreateAccountOnlyByRegistrar {
account_id: account_id.clone(),
registrar_account_id: account_creation_config.registrar_account_id.clone(),
predecessor_id: predecessor_id.clone(),
}
.into());
return;
}
} else {
// OK: Valid top-level Account ID
}
Expand Down Expand Up @@ -967,6 +985,7 @@ mod tests {
use near_primitives::transaction::CreateAccountAction;
use near_primitives::trie_key::TrieKey;
use near_primitives::types::{EpochId, StateChangeCause};
use near_primitives::version::PROTOCOL_VERSION;
use near_store::set_account;
use near_store::test_utils::create_tries;
use std::sync::Arc;
Expand All @@ -990,6 +1009,7 @@ mod tests {
&account_id,
&predecessor_id,
&mut action_result,
PROTOCOL_VERSION,
);
if action_result.result.is_ok() {
assert!(account.is_some());
Expand Down Expand Up @@ -1069,6 +1089,28 @@ mod tests {
assert!(action_result.result.is_ok());
}

#[test]
#[cfg(feature = "protocol_feature_ethereum_address")]
fn test_create_ethereum_address() {
let account_id: AccountId = "0x32400084c286cf3e17e7b677ea9583e60a000324".parse().unwrap();
let predecessor_id: AccountId = "near".parse().unwrap();
let action_result = test_action_create_account(account_id.clone(), predecessor_id.clone(), 32);
assert_eq!(
action_result.result,
Err(ActionError {
index: None,
kind: ActionErrorKind::CreateAccountOnlyByRegistrar {
account_id: account_id.clone(),
registrar_account_id: "registrar".parse().unwrap(),
predecessor_id: predecessor_id,
},
})
);
let registrar_account_id: AccountId = "registrar".parse().unwrap();
let action_result = test_action_create_account(account_id, registrar_account_id, 32);
assert!(action_result.result.is_ok());
}

fn test_delete_large_account(
account_id: &AccountId,
code_hash: &CryptoHash,
Expand Down
1 change: 1 addition & 0 deletions runtime/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ impl Runtime {
&receipt.receiver_id,
&receipt.predecessor_id,
&mut result,
apply_state.current_protocol_version,
);
}
Action::DeployContract(deploy_contract) => {
Expand Down

0 comments on commit 62367ed

Please sign in to comment.