From 7cb592f4323072a7493c05be0ca571db2f0a2612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Chuda=C5=9B?= Date: Tue, 7 Nov 2023 17:21:47 +0100 Subject: [PATCH] Wrap changes in protocol_feature --- core/primitives-core/Cargo.toml | 2 + core/primitives-core/src/runtime/fees.rs | 12 +- core/primitives-core/src/version.rs | 6 +- core/primitives/Cargo.toml | 2 + core/primitives/src/test_utils.rs | 13 +- core/primitives/src/utils.rs | 12 ++ integration-tests/Cargo.toml | 4 + .../access_key_nonce_for_implicit_accounts.rs | 7 +- .../tests/client/features/delegate_action.rs | 58 +++++--- .../src/tests/standard_cases/runtime.rs | 3 + integration-tests/src/user/mod.rs | 20 ++- nearcore/Cargo.toml | 4 + neard/Cargo.toml | 2 + runtime/runtime/Cargo.toml | 5 + runtime/runtime/src/actions.rs | 127 +++++++++--------- runtime/runtime/src/verifier.rs | 79 +++++------ tools/fork-network/src/cli.rs | 19 +-- tools/mirror/src/genesis.rs | 19 +-- tools/mirror/src/lib.rs | 5 +- 19 files changed, 246 insertions(+), 153 deletions(-) diff --git a/core/primitives-core/Cargo.toml b/core/primitives-core/Cargo.toml index 2d7ff4c30ae..76d7ab76939 100644 --- a/core/primitives-core/Cargo.toml +++ b/core/primitives-core/Cargo.toml @@ -37,10 +37,12 @@ protocol_feature_fix_contract_loading_cost = [] protocol_feature_reject_blocks_with_outdated_protocol_version = [] protocol_feature_simple_nightshade_v2 = [] protocol_feature_chunk_validation = [] +protocol_feature_eth_implicit = [] nightly = [ "nightly_protocol", "protocol_feature_chunk_validation", + "protocol_feature_eth_implicit", "protocol_feature_fix_contract_loading_cost", "protocol_feature_fix_staking_threshold", "protocol_feature_reject_blocks_with_outdated_protocol_version", diff --git a/core/primitives-core/src/runtime/fees.rs b/core/primitives-core/src/runtime/fees.rs index 818319eac27..b808127781c 100644 --- a/core/primitives-core/src/runtime/fees.rs +++ b/core/primitives-core/src/runtime/fees.rs @@ -219,7 +219,10 @@ pub fn transfer_exec_fee( (false, _) => transfer_fee, // Extra fee for the CreateAccount. (true, AccountType::EthImplicitAccount) => { - transfer_fee + cfg.fee(ActionCosts::create_account).exec_fee() + #[cfg(feature = "protocol_feature_eth_implicit")] + return transfer_fee + cfg.fee(ActionCosts::create_account).exec_fee(); + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + transfer_fee } // Extra fees for the CreateAccount and AddFullAccessKey. (true, AccountType::NearImplicitAccount) => { @@ -244,7 +247,12 @@ pub fn transfer_send_fee( (false, _) => transfer_fee, // Extra fee for the CreateAccount. (true, AccountType::EthImplicitAccount) => { - transfer_fee + cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver) + #[cfg(feature = "protocol_feature_eth_implicit")] + { + transfer_fee + cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver) + } + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + transfer_fee } // Extra fees for the CreateAccount and AddFullAccessKey. (true, AccountType::NearImplicitAccount) => { diff --git a/core/primitives-core/src/version.rs b/core/primitives-core/src/version.rs index f342479f53b..1327d64a2bc 100644 --- a/core/primitives-core/src/version.rs +++ b/core/primitives-core/src/version.rs @@ -132,6 +132,8 @@ pub enum ProtocolFeature { /// NEP: https://github.com/near/NEPs/pull/509 #[cfg(feature = "protocol_feature_chunk_validation")] ChunkValidation, + #[cfg(feature = "protocol_feature_eth_implicit")] + EthImplicit, } impl ProtocolFeature { @@ -189,6 +191,8 @@ impl ProtocolFeature { ProtocolFeature::PostStateRoot => 136, #[cfg(feature = "protocol_feature_chunk_validation")] ProtocolFeature::ChunkValidation => 137, + #[cfg(feature = "protocol_feature_eth_implicit")] + ProtocolFeature::EthImplicit => 138, } } } @@ -201,7 +205,7 @@ const STABLE_PROTOCOL_VERSION: ProtocolVersion = 64; /// 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. - 139 + 140 } else { // Enable all stable features. STABLE_PROTOCOL_VERSION diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index 74082351104..725780173a7 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -51,10 +51,12 @@ protocol_feature_fix_staking_threshold = ["near-primitives-core/protocol_feature 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_eth_implicit = ["near-primitives-core/protocol_feature_eth_implicit"] protocol_feature_chunk_validation = [] nightly = [ "nightly_protocol", "protocol_feature_chunk_validation", + "protocol_feature_eth_implicit", "protocol_feature_fix_contract_loading_cost", "protocol_feature_fix_staking_threshold", "protocol_feature_reject_blocks_with_outdated_protocol_version", diff --git a/core/primitives/src/test_utils.rs b/core/primitives/src/test_utils.rs index 327dc1c1524..4cb47d85a79 100644 --- a/core/primitives/src/test_utils.rs +++ b/core/primitives/src/test_utils.rs @@ -557,12 +557,13 @@ pub fn create_test_signer(account_name: &str) -> InMemoryValidatorSigner { pub fn create_user_test_signer(account_name: &AccountIdRef) -> InMemorySigner { let account_id = account_name.to_owned(); if account_id == near_implicit_test_account() { - InMemorySigner::from_secret_key(account_id, near_implicit_test_account_secret()) - } else if account_id == eth_implicit_test_account() { - InMemorySigner::from_secret_key(account_id, eth_implicit_test_account_secret()) - } else { - InMemorySigner::from_seed(account_id, KeyType::ED25519, account_name.as_str()) + return InMemorySigner::from_secret_key(account_id, near_implicit_test_account_secret()); } + #[cfg(feature = "protocol_feature_eth_implicit")] + if account_id == eth_implicit_test_account() { + return InMemorySigner::from_secret_key(account_id, eth_implicit_test_account_secret()); + } + InMemorySigner::from_seed(account_id, KeyType::ED25519, account_name.as_str()) } /// A fixed NEAR-implicit account for which tests can know the private key. @@ -576,11 +577,13 @@ pub fn near_implicit_test_account_secret() -> SecretKey { } /// A fixed ETH-implicit account for which tests can know the private key. +#[cfg(feature = "protocol_feature_eth_implicit")] pub fn eth_implicit_test_account() -> AccountId { "0x96791e923f8cf697ad9c3290f2c9059f0231b24c".parse().unwrap() } /// Private key for the fixed ETH-implicit test account. +#[cfg(feature = "protocol_feature_eth_implicit")] pub fn eth_implicit_test_account_secret() -> SecretKey { "secp256k1:X4ETFKtQkSGVoZEnkn7bZ3LyajJaK2b3eweXaKmynGx".parse().unwrap() } diff --git a/core/primitives/src/utils.rs b/core/primitives/src/utils.rs index 91d4f25d869..1bfd6c99bc3 100644 --- a/core/primitives/src/utils.rs +++ b/core/primitives/src/utils.rs @@ -19,6 +19,8 @@ use crate::version::{ use near_crypto::{KeyType, PublicKey}; use near_primitives_core::account::id::AccountId; +#[cfg(not(feature = "protocol_feature_eth_implicit"))] +use near_primitives_core::account::id::AccountType; use std::mem::size_of; use std::ops::Deref; @@ -469,6 +471,16 @@ where Serializable(object) } +/// From `near-account-id` version `1.0.0-alpha.2`, `is_implicit` returns true for ETH-implicit accounts. +/// This function is a wrapper for `is_implicit` method so that we can easily differentiate its behavior +/// based on the protocol version. +pub fn account_is_implicit(account_id: &AccountId) -> bool { + #[cfg(feature = "protocol_feature_eth_implicit")] + return account_id.get_account_type().is_implicit(); + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + return account_id.get_account_type() == AccountType::NearImplicitAccount; +} + /// Derives `AccountId` from `PublicKey`. /// If the key type is ED25519, returns hex-encoded copy of the key. /// If the key type is SECP256K1, returns '0x' + keccak256(public_key)[12:32].hex(). diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 29a9e6ef6c3..0e08a85af1f 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -80,9 +80,13 @@ protocol_feature_reject_blocks_with_outdated_protocol_version = [ protocol_feature_simple_nightshade_v2 = [ "near-primitives/protocol_feature_simple_nightshade_v2", ] +protocol_feature_eth_implicit = [ + "near-primitives/protocol_feature_eth_implicit", +] nightly = [ "nightly_protocol", + "protocol_feature_eth_implicit", "protocol_feature_fix_contract_loading_cost", "protocol_feature_reject_blocks_with_outdated_protocol_version", "protocol_feature_simple_nightshade_v2", diff --git a/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs b/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs index 5c784fa7953..618e8cabe82 100644 --- a/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs +++ b/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs @@ -201,6 +201,7 @@ fn get_status_of_tx_hash_collision_for_implicit_account( /// Test that duplicate transactions from ETH-implicit account can be properly rejected /// if we set nonce to `(block_height - 1) * 1e6` for transactions that results in /// access key being added to the ETH-implicit account. +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn test_transaction_eth_implicit_account() { let epoch_length = 10; @@ -337,6 +338,7 @@ fn test_transaction_eth_implicit_account() { } /// Test that the signer is correctly verified for transactions done from an ETH-implicit account. +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn test_transaction_eth_implicit_account_invalid_pk() { let epoch_length = 10; @@ -417,9 +419,9 @@ fn test_transaction_eth_implicit_account_invalid_pk() { // Now we send money from ETH-implicit account using correct signing key. // As this is the first valid transaction from that account, no access key has been added yet, - // so using 0 nonce should pass (we should not use 0 nonce in real transaction). + // so using nonce=1 should pass (however, we should use `block_height - 1) * 1e6` in a real transaction). let send_money_from_implicit_account_tx = SignedTransaction::send_money( - 0, + 1, implicit_account_id, "test0".parse().unwrap(), &implicit_account_signer, @@ -465,6 +467,7 @@ fn test_transaction_hash_collision_for_near_implicit_account_ok() { /// Test that duplicate transactions from ETH-implicit accounts are not rejected before and after protocol upgrade. /// It is responsibility of the transaction signer to choose nonce equal to `(block_height - 1) * 1e6` in case the transaction /// results in adding a new key to an ETH-implicit account (see https://github.com/near/NEPs/issues/498#issuecomment-1782881395). +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn test_transaction_hash_collision_for_eth_implicit_account_ok() { let protocol_version = diff --git a/integration-tests/src/tests/client/features/delegate_action.rs b/integration-tests/src/tests/client/features/delegate_action.rs index 405a43a070b..668e612bb83 100644 --- a/integration-tests/src/tests/client/features/delegate_action.rs +++ b/integration-tests/src/tests/client/features/delegate_action.rs @@ -17,10 +17,9 @@ use near_primitives::errors::{ ActionError, ActionErrorKind, ActionsValidationError, InvalidAccessKeyError, InvalidTxError, TxExecutionError, }; -use near_primitives::test_utils::{ - create_user_test_signer, eth_implicit_test_account, eth_implicit_test_account_secret, - near_implicit_test_account, -}; +use near_primitives::test_utils::{create_user_test_signer, near_implicit_test_account}; +#[cfg(feature = "protocol_feature_eth_implicit")] +use near_primitives::test_utils::{eth_implicit_test_account, eth_implicit_test_account_secret}; use near_primitives::transaction::{ Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction, FunctionCallAction, StakeAction, TransferAction, @@ -111,6 +110,29 @@ fn reject_valid_meta_tx_in_older_versions() { ); } +/// For NEAR-implicit accounts, returns the corresponding implicit public key. +/// For Named accounts, generates a public key using the account ID as the seed. +/// +/// In case of ETH-implicit account, the ETH-implicit address shall be derived from the public key. +/// Thus, we cannot derive the public key from ETH-implicit address and the caller must +/// explicitly provide the Secp256K1 `public_key` matching the sender address. +fn derive_public_key_from_sender(sender: &AccountId, public_key: Option) -> PublicKey { + match sender.get_account_type() { + AccountType::NearImplicitAccount => PublicKey::from_near_implicit_account(&sender).unwrap(), + AccountType::EthImplicitAccount => { + // We require that tests sending transactions from ETH-implicit accounts must provide the public key. + #[cfg(feature = "protocol_feature_eth_implicit")] + { + public_key.unwrap() + } + + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + PublicKey::from_seed(KeyType::ED25519, sender.as_ref()) + } + AccountType::NamedAccount => PublicKey::from_seed(KeyType::ED25519, sender.as_ref()), + } +} + /// Take a list of actions and execute them as a meta transaction, check /// everything executes successfully, return balance differences for the sender, /// relayer, and receiver. @@ -141,16 +163,9 @@ fn check_meta_tx_execution( .unwrap() .nonce; - let user_pubk = match sender.get_account_type() { - AccountType::NearImplicitAccount => PublicKey::from_near_implicit_account(&sender).unwrap(), - // We require that tests sending transactions from ETH-implicit accounts provide the public key. - // It is because we can't infer public key from ETH-implicit account ID, and we need - // the public key from which the ETH-implicit sender address was derived. - AccountType::EthImplicitAccount => public_key.unwrap(), - AccountType::NamedAccount => PublicKey::from_seed(KeyType::ED25519, sender.as_ref()), - }; + let user_pubk = derive_public_key_from_sender(&sender, public_key); - let user_nonce_before = node_user.get_access_key(&sender, &user_pubk); + let access_key_before = node_user.get_access_key(&sender, &user_pubk); let tx_result = node_user.meta_tx(sender.clone(), receiver.clone(), relayer.clone(), actions).unwrap(); @@ -165,17 +180,23 @@ fn check_meta_tx_execution( .nonce; assert_eq!(relayer_nonce, relayer_nonce_before + 1); - match user_nonce_before { - Ok(user_nonce_before) => { + match access_key_before { + Ok(access_key_before) => { // user key must be checked for existence (to test DeleteKey action) if let Ok(user_nonce) = node_user .get_access_key(&sender, &PublicKey::from_seed(KeyType::ED25519, sender.as_ref())) .map(|key| key.nonce) { - assert_eq!(user_nonce, user_nonce_before.nonce + 1); + assert_eq!(user_nonce, access_key_before.nonce + 1); } } - Err(_) => assert!(sender.get_account_type() == AccountType::EthImplicitAccount), + Err(_) => { + #[cfg(feature = "protocol_feature_eth_implicit")] + assert!(sender.get_account_type() == AccountType::EthImplicitAccount); + + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + panic!("Sender account must have access key"); + } } let sender_after = node_user.view_balance(&sender).unwrap_or(0); @@ -829,6 +850,7 @@ fn meta_tx_create_near_implicit_account_fails() { meta_tx_create_implicit_account_fails(near_implicit_test_account()); } +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn meta_tx_create_eth_implicit_account_fails() { meta_tx_create_implicit_account_fails(eth_implicit_test_account()); @@ -871,6 +893,7 @@ fn meta_tx_create_and_use_near_implicit_account() { meta_tx_create_and_use_implicit_account(near_implicit_test_account()); } +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn meta_tx_create_and_use_eth_implicit_account() { meta_tx_create_and_use_implicit_account(eth_implicit_test_account()); @@ -952,6 +975,7 @@ fn meta_tx_create_near_implicit_account() { meta_tx_create_implicit_account(near_implicit_test_account(), None); } +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn meta_tx_create_eth_implicit_account() { let secret_key = eth_implicit_test_account_secret(); diff --git a/integration-tests/src/tests/standard_cases/runtime.rs b/integration-tests/src/tests/standard_cases/runtime.rs index 5fe99b9d2e7..6f8a459cbfd 100644 --- a/integration-tests/src/tests/standard_cases/runtime.rs +++ b/integration-tests/src/tests/standard_cases/runtime.rs @@ -1,6 +1,7 @@ use crate::node::RuntimeNode; use crate::tests::standard_cases::*; use near_chain_configs::Genesis; +#[cfg(feature = "protocol_feature_eth_implicit")] use near_crypto::SecretKey; use near_primitives::state_record::StateRecord; use nearcore::config::{GenesisExt, TESTING_INIT_BALANCE}; @@ -121,6 +122,7 @@ fn test_transfer_tokens_near_implicit_account_runtime() { transfer_tokens_implicit_account(node, public_key); } +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn test_transfer_tokens_eth_implicit_account_runtime() { let node = create_runtime_node(); @@ -135,6 +137,7 @@ fn test_trying_to_create_near_implicit_account_runtime() { trying_to_create_implicit_account(node, public_key); } +#[cfg(feature = "protocol_feature_eth_implicit")] #[test] fn test_trying_to_create_eth_implicit_account_runtime() { let node = create_runtime_node(); diff --git a/integration-tests/src/user/mod.rs b/integration-tests/src/user/mod.rs index d5beec891ce..319d08358d3 100644 --- a/integration-tests/src/user/mod.rs +++ b/integration-tests/src/user/mod.rs @@ -4,7 +4,9 @@ use futures::{future::LocalBoxFuture, FutureExt}; use near_crypto::{PublicKey, Signer}; use near_jsonrpc_primitives::errors::ServerError; -use near_primitives::account::{id::AccountType, AccessKey}; +#[cfg(feature = "protocol_feature_eth_implicit")] +use near_primitives::account::id::AccountType; +use near_primitives::account::AccessKey; use near_primitives::action::delegate::{DelegateAction, NonDelegateAction, SignedDelegateAction}; use near_primitives::hash::CryptoHash; use near_primitives::receipt::Receipt; @@ -266,11 +268,17 @@ pub trait User { let user_nonce = match access_key { Ok(access_key) => access_key.nonce, - Err(_) => match signer_id.get_account_type() { - // Zero nonce is for tests only. In real setting we should use `(block_height - 1) * 1e6`. - AccountType::EthImplicitAccount => 0, - _ => panic!("failed reading user's nonce for access key"), - }, + Err(_) => { + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + panic!("failed reading user's nonce for access key"); + + #[cfg(feature = "protocol_feature_eth_implicit")] + match signer_id.get_account_type() { + // Zero nonce is for tests only. In real setting we should use `(block_height - 1) * 1e6`. + AccountType::EthImplicitAccount => 0, + _ => panic!("failed reading user's nonce for access key"), + } + } }; let delegate_action = DelegateAction { diff --git a/nearcore/Cargo.toml b/nearcore/Cargo.toml index 81d2db6b189..cfeccab3e46 100644 --- a/nearcore/Cargo.toml +++ b/nearcore/Cargo.toml @@ -115,6 +115,9 @@ protocol_feature_fix_contract_loading_cost = [ protocol_feature_simple_nightshade_v2 = [ "near-primitives/protocol_feature_simple_nightshade_v2", ] +protocol_feature_eth_implicit = [ + "near-primitives/protocol_feature_eth_implicit", +] new_epoch_sync = [ "near-client/new_epoch_sync" ] @@ -122,6 +125,7 @@ new_epoch_sync = [ serialize_all_state_changes = ["near-store/serialize_all_state_changes"] nightly = [ "nightly_protocol", + "protocol_feature_eth_implicit", "protocol_feature_fix_contract_loading_cost", "protocol_feature_fix_staking_threshold", "protocol_feature_simple_nightshade_v2", diff --git a/neard/Cargo.toml b/neard/Cargo.toml index 44ee07b1a72..9235469412d 100644 --- a/neard/Cargo.toml +++ b/neard/Cargo.toml @@ -71,11 +71,13 @@ rosetta_rpc = ["nearcore/rosetta_rpc"] json_rpc = ["nearcore/json_rpc"] protocol_feature_fix_staking_threshold = ["nearcore/protocol_feature_fix_staking_threshold"] protocol_feature_simple_nightshade_v2 = ["nearcore/protocol_feature_simple_nightshade_v2"] +protocol_feature_eth_implicit = ["nearcore/protocol_feature_eth_implicit"] serialize_all_state_changes = ["nearcore/serialize_all_state_changes"] new_epoch_sync = ["nearcore/new_epoch_sync"] nightly = [ "nightly_protocol", + "protocol_feature_eth_implicit", "protocol_feature_fix_staking_threshold", "protocol_feature_simple_nightshade_v2", "serialize_all_state_changes", diff --git a/runtime/runtime/Cargo.toml b/runtime/runtime/Cargo.toml index 58e79b8abc3..b034e920e3a 100644 --- a/runtime/runtime/Cargo.toml +++ b/runtime/runtime/Cargo.toml @@ -32,8 +32,13 @@ near-store.workspace = true near-vm-runner.workspace = true [features] +protocol_feature_eth_implicit = [ + "near-primitives/protocol_feature_eth_implicit", +] + nightly = [ "nightly_protocol", + "protocol_feature_eth_implicit", "near-chain-configs/nightly", "near-o11y/nightly", "near-primitives-core/nightly", diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index a3516cc86f9..c1c15314a77 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -22,7 +22,9 @@ use near_primitives::transaction::{ }; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{AccountId, BlockHeight, EpochInfoProvider, Gas, TrieCacheMode}; -use near_primitives::utils::{create_random_seed, derive_account_id_from_public_key}; +#[cfg(feature = "protocol_feature_eth_implicit")] +use near_primitives::utils::derive_account_id_from_public_key; +use near_primitives::utils::{account_is_implicit, create_random_seed}; use near_primitives::version::{ ProtocolFeature, ProtocolVersion, DELETE_KEY_STORAGE_USAGE_PROTOCOL_VERSION, }; @@ -474,13 +476,19 @@ pub(crate) fn action_implicit_account_creation_transfer( // Invariant: The `account_id` is implicit. // It holds because in the only calling site, we've checked the permissions before. AccountType::EthImplicitAccount => { - *account = Some(Account::new( - transfer.deposit, - 0, - CryptoHash::default(), - fee_config.storage_usage_config.num_bytes_account - + fee_config.storage_usage_config.num_extra_bytes_record, - )); + #[cfg(feature = "protocol_feature_eth_implicit")] + { + *account = Some(Account::new( + transfer.deposit, + 0, + CryptoHash::default(), + fee_config.storage_usage_config.num_bytes_account + + fee_config.storage_usage_config.num_extra_bytes_record, + )); + } + + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + panic!("must be near-implicit") } AccountType::NamedAccount => panic!("must be implicit"), } @@ -745,58 +753,57 @@ fn validate_delegate_action_key( result: &mut ActionResult, ) -> Result<(), RuntimeError> { // 'delegate_action.sender_id' account existence must be checked by a caller - let signer_id = &delegate_action.sender_id; - let public_key = &delegate_action.public_key; - // By default, we always check nonce. - let mut should_check_nonce = true; - let mut access_key = - match get_access_key(state_update, &signer_id, &delegate_action.public_key)? { - Some(access_key) => access_key, - None => match signer_id.get_account_type() { - // Access key is missing, but in the case of a transaction from ETH-implicit account - // there is a possibility that full access key will be created now. - AccountType::EthImplicitAccount => { - if derive_account_id_from_public_key(public_key) == *signer_id { - // This is the only case we do not compare access key nonce with transaction nonce, - // because the access key is created now. - should_check_nonce = false; - // TODO What about increasing storage usage for that account, because we added access key? - AccessKey::full_access() - } else { - // Provided public key is not the one from which the signer address was derived. - result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( - InvalidAccessKeyError::InvalidPkForEthAddress { - account_id: signer_id.clone(), - public_key: public_key.clone(), - }, - ) - .into()); - return Ok(()); - } - } - // Transactions done from non-ETH-implicit account must have access key already added. - _ => { - result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( - InvalidAccessKeyError::AccessKeyNotFound { - account_id: signer_id.clone(), - public_key: public_key.clone(), - }, - ) - .into()); - return Ok(()); - } - }, - }; - - if should_check_nonce { - if delegate_action.nonce <= access_key.nonce { - result.result = Err(ActionErrorKind::DelegateActionInvalidNonce { - delegate_nonce: delegate_action.nonce, - ak_nonce: access_key.nonce, + let sender_id = &delegate_action.sender_id; + let public_key: &PublicKey = &delegate_action.public_key; + + #[cfg(feature = "protocol_feature_eth_implicit")] + let mut access_key = get_access_key(state_update, &sender_id, &public_key)?; + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + let access_key = get_access_key(state_update, &sender_id, &public_key)?; + + #[cfg(feature = "protocol_feature_eth_implicit")] + { + // Access key is missing, but in the case of a transaction from ETH-implicit account + // there is a possibility that full access key will be created now. + if access_key.is_none() && sender_id.get_account_type() == AccountType::EthImplicitAccount { + if derive_account_id_from_public_key(public_key) == *sender_id { + // TODO What about increasing storage usage for that account, because we added access key? + access_key = Some(AccessKey::full_access()); + } else { + // Provided public key is not the one from which the signer address was derived. + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::InvalidPkForEthAddress { + account_id: sender_id.clone(), + public_key: public_key.clone(), + }, + ) + .into()); + return Ok(()); } + } + } + + let mut access_key = match access_key { + Some(access_key) => access_key, + None => { + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: sender_id.clone(), + public_key: public_key.clone(), + }, + ) .into()); return Ok(()); } + }; + + if delegate_action.nonce <= access_key.nonce { + result.result = Err(ActionErrorKind::DelegateActionInvalidNonce { + delegate_nonce: delegate_action.nonce, + ak_nonce: access_key.nonce, + } + .into()); + return Ok(()); } let upper_bound = apply_state.block_height @@ -865,7 +872,7 @@ fn validate_delegate_action_key( } }; - set_access_key(state_update, signer_id.clone(), public_key.clone(), &access_key); + set_access_key(state_update, sender_id.clone(), public_key.clone(), &access_key); Ok(()) } @@ -925,9 +932,7 @@ pub(crate) fn check_account_existence( .into()); } else { // TODO: this should be `config.implicit_account_creation`. - if config.wasm_config.implicit_account_creation - && account_id.get_account_type().is_implicit() - { + if config.wasm_config.implicit_account_creation && account_is_implicit(account_id) { // If the account doesn't exist and it's implicit, then you // should only be able to create it using single transfer action. // Because you should not be able to add another access key to the account in @@ -948,7 +953,7 @@ pub(crate) fn check_account_existence( if account.is_none() { return if config.wasm_config.implicit_account_creation && is_the_only_action - && account_id.get_account_type().is_implicit() + && account_is_implicit(account_id) && !is_refund { // OK. It's implicit account creation. diff --git a/runtime/runtime/src/verifier.rs b/runtime/runtime/src/verifier.rs index 17cbf60e2ce..b6d28a858ae 100644 --- a/runtime/runtime/src/verifier.rs +++ b/runtime/runtime/src/verifier.rs @@ -2,7 +2,9 @@ use crate::config::{total_prepaid_gas, tx_cost, TransactionCost}; use crate::near_primitives::account::Account; use crate::VerificationResult; use near_crypto::key_conversion::is_valid_staking_key; -use near_primitives::account::{AccessKey, AccessKeyPermission}; +#[cfg(feature = "protocol_feature_eth_implicit")] +use near_primitives::account::AccessKey; +use near_primitives::account::AccessKeyPermission; use near_primitives::action::delegate::SignedDelegateAction; use near_primitives::checked_feature; use near_primitives::errors::{ @@ -17,9 +19,11 @@ use near_primitives::transaction::{ }; use near_primitives::types::{AccountId, Balance}; use near_primitives::types::{BlockHeight, StorageUsage}; +#[cfg(feature = "protocol_feature_eth_implicit")] use near_primitives::utils::derive_account_id_from_public_key; use near_primitives::version::ProtocolFeature; use near_primitives::version::ProtocolVersion; +#[cfg(feature = "protocol_feature_eth_implicit")] use near_primitives_core::account::id::AccountType; use near_store::{ get_access_key, get_account, set_access_key, set_account, StorageError, TrieUpdate, @@ -154,54 +158,53 @@ pub fn verify_and_charge_transaction( return Err(InvalidTxError::SignerDoesNotExist { signer_id: signer_id.clone() }.into()); } }; - // By default, we always check nonce. - let mut should_check_nonce = true; - let mut access_key = match get_access_key(state_update, signer_id, public_key)? { - Some(access_key) => access_key, - None => match signer_id.get_account_type() { - // Access key is missing, but in the case of a transaction from ETH-implicit account - // there is a possibility that full access key will be created now. - AccountType::EthImplicitAccount => { - if derive_account_id_from_public_key(public_key) == *signer_id { - // This is the only case we do not compare access key nonce with transaction nonce, - // because the access key is created now. - should_check_nonce = false; - // TODO What about increasing storage usage for that account, because we added access key? - AccessKey::full_access() - } else { - // Provided public key is not the one from which the signer address was derived. - return Err(InvalidTxError::InvalidAccessKeyError( - InvalidAccessKeyError::InvalidPkForEthAddress { - account_id: signer_id.clone(), - public_key: public_key.clone(), - }, - ) - .into()); - } - } - // Transactions done from non-ETH-implicit account must have access key already added. - _ => { + + #[cfg(feature = "protocol_feature_eth_implicit")] + let mut access_key = get_access_key(state_update, signer_id, public_key)?; + #[cfg(not(feature = "protocol_feature_eth_implicit"))] + let access_key = get_access_key(state_update, signer_id, public_key)?; + + #[cfg(feature = "protocol_feature_eth_implicit")] + { + // Access key is missing, but in the case of a transaction from ETH-implicit account + // there is a possibility that full access key will be created now. + if access_key.is_none() && signer_id.get_account_type() == AccountType::EthImplicitAccount { + if derive_account_id_from_public_key(public_key) == *signer_id { + // TODO What about increasing storage usage for that account, because we added access key? + access_key = Some(AccessKey::full_access()); + } else { + // Provided public key is not the one from which the signer address was derived. return Err(InvalidTxError::InvalidAccessKeyError( - InvalidAccessKeyError::AccessKeyNotFound { + InvalidAccessKeyError::InvalidPkForEthAddress { account_id: signer_id.clone(), public_key: public_key.clone(), }, ) .into()); } - }, - }; + } + } - if should_check_nonce { - if transaction.nonce <= access_key.nonce { - return Err(InvalidTxError::InvalidNonce { - tx_nonce: transaction.nonce, - ak_nonce: access_key.nonce, - } + let mut access_key = match access_key { + Some(access_key) => access_key, + None => { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: signer_id.clone(), + public_key: public_key.clone(), + }, + ) .into()); } - } + }; + if transaction.nonce <= access_key.nonce { + return Err(InvalidTxError::InvalidNonce { + tx_nonce: transaction.nonce, + ak_nonce: access_key.nonce, + } + .into()); + } if checked_feature!("stable", AccessKeyNonceRange, current_protocol_version) { if let Some(height) = block_height { let upper_bound = diff --git a/tools/fork-network/src/cli.rs b/tools/fork-network/src/cli.rs index 63b2d7ff57e..56dfda7d64d 100644 --- a/tools/fork-network/src/cli.rs +++ b/tools/fork-network/src/cli.rs @@ -22,6 +22,7 @@ use near_primitives::trie_key::trie_key_parsers::parse_account_id_from_account_k use near_primitives::types::{ AccountId, AccountInfo, Balance, BlockHeight, EpochId, NumBlocks, ShardId, StateRoot, }; +use near_primitives::utils::account_is_implicit; use near_primitives::version::PROTOCOL_VERSION; use near_store::db::RocksDB; use near_store::flat::FlatStorageStatus; @@ -486,7 +487,7 @@ impl ForkNetworkCommand { if let Some(sr) = StateRecord::from_raw_key_value(key.clone(), value.clone()) { match sr { StateRecord::AccessKey { account_id, public_key, access_key } => { - if !account_id.get_account_type().is_implicit() + if !account_is_implicit(&account_id) && access_key.permission == AccessKeyPermission::FullAccess { has_full_key.insert(account_id.clone()); @@ -503,7 +504,7 @@ impl ForkNetworkCommand { } StateRecord::Account { account_id, account } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(&account_id) { let new_account_id = map_account(&account_id, None); storage_mutator.delete_account(account_id)?; storage_mutator.set_account(new_account_id, account)?; @@ -511,7 +512,7 @@ impl ForkNetworkCommand { } } StateRecord::Data { account_id, data_key, value } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(&account_id) { let new_account_id = map_account(&account_id, None); storage_mutator.delete_data(account_id, &data_key)?; storage_mutator.set_data(new_account_id, &data_key, value)?; @@ -519,7 +520,7 @@ impl ForkNetworkCommand { } } StateRecord::Contract { account_id, code } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(&account_id) { let new_account_id = map_account(&account_id, None); storage_mutator.delete_code(account_id)?; storage_mutator.set_code(new_account_id, code)?; @@ -527,8 +528,8 @@ impl ForkNetworkCommand { } } StateRecord::PostponedReceipt(receipt) => { - if receipt.predecessor_id.get_account_type().is_implicit() - || receipt.receiver_id.get_account_type().is_implicit() + if account_is_implicit(&receipt.predecessor_id) + || account_is_implicit(&receipt.receiver_id) { let new_receipt = Receipt { predecessor_id: map_account(&receipt.predecessor_id, None), @@ -542,7 +543,7 @@ impl ForkNetworkCommand { } } StateRecord::ReceivedData { account_id, data_id, data } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(&account_id) { let new_account_id = map_account(&account_id, None); storage_mutator.delete_received_data(account_id, data_id)?; storage_mutator.set_received_data(new_account_id, data_id, &data)?; @@ -550,8 +551,8 @@ impl ForkNetworkCommand { } } StateRecord::DelayedReceipt(receipt) => { - if receipt.predecessor_id.get_account_type().is_implicit() - || receipt.receiver_id.get_account_type().is_implicit() + if account_is_implicit(&receipt.predecessor_id) + || account_is_implicit(&receipt.receiver_id) { let new_receipt = Receipt { predecessor_id: map_account(&receipt.predecessor_id, None), diff --git a/tools/mirror/src/genesis.rs b/tools/mirror/src/genesis.rs index 6f354d8df76..e48c794dd92 100644 --- a/tools/mirror/src/genesis.rs +++ b/tools/mirror/src/genesis.rs @@ -1,4 +1,5 @@ use near_primitives::state_record::StateRecord; +use near_primitives::utils::account_is_implicit; use near_primitives_core::account::{AccessKey, AccessKeyPermission}; use serde::ser::{SerializeSeq, Serializer}; use std::collections::HashSet; @@ -38,7 +39,7 @@ pub fn map_records>( public_key: replacement.public_key(), access_key: access_key.clone(), }; - if !account_id.get_account_type().is_implicit() + if !account_is_implicit(account_id) && access_key.permission == AccessKeyPermission::FullAccess { has_full_key.insert(account_id.clone()); @@ -48,7 +49,7 @@ pub fn map_records>( records_seq.serialize_element(&new_record).unwrap(); } StateRecord::Account { account_id, .. } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(account_id) { *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); } else { accounts.insert(account_id.clone()); @@ -56,20 +57,20 @@ pub fn map_records>( records_seq.serialize_element(&r).unwrap(); } StateRecord::Data { account_id, .. } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(account_id) { *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); } records_seq.serialize_element(&r).unwrap(); } StateRecord::Contract { account_id, .. } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(account_id) { *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); } records_seq.serialize_element(&r).unwrap(); } StateRecord::PostponedReceipt(receipt) => { - if receipt.predecessor_id.get_account_type().is_implicit() - || receipt.receiver_id.get_account_type().is_implicit() + if account_is_implicit(&receipt.predecessor_id) + || account_is_implicit(&receipt.receiver_id) { receipt.predecessor_id = crate::key_mapping::map_account(&receipt.predecessor_id, secret.as_ref()); @@ -79,14 +80,14 @@ pub fn map_records>( records_seq.serialize_element(&r).unwrap(); } StateRecord::ReceivedData { account_id, .. } => { - if account_id.get_account_type().is_implicit() { + if account_is_implicit(account_id) { *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); } records_seq.serialize_element(&r).unwrap(); } StateRecord::DelayedReceipt(receipt) => { - if receipt.predecessor_id.get_account_type().is_implicit() - || receipt.receiver_id.get_account_type().is_implicit() + if account_is_implicit(&receipt.predecessor_id) + || account_is_implicit(&receipt.receiver_id) { receipt.predecessor_id = crate::key_mapping::map_account(&receipt.predecessor_id, secret.as_ref()); diff --git a/tools/mirror/src/lib.rs b/tools/mirror/src/lib.rs index 6f829c2161f..8be2adc51ed 100644 --- a/tools/mirror/src/lib.rs +++ b/tools/mirror/src/lib.rs @@ -22,6 +22,7 @@ use near_primitives::transaction::{ use near_primitives::types::{ AccountId, BlockHeight, BlockReference, Finality, TransactionOrReceiptId, }; +use near_primitives::utils::account_is_implicit; use near_primitives::views::{ ExecutionOutcomeWithIdView, ExecutionStatusView, QueryRequest, QueryResponseKind, SignedTransactionView, @@ -990,9 +991,7 @@ impl TxMirror { actions.push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); } Action::Transfer(_) => { - if tx.receiver_id().get_account_type().is_implicit() - && source_actions.len() == 1 - { + if account_is_implicit(tx.receiver_id()) && source_actions.len() == 1 { let target_account = crate::key_mapping::map_account(tx.receiver_id(), self.secret.as_ref()); if !account_exists(&self.target_view_client, &target_account)