diff --git a/core/account-id/src/lib.rs b/core/account-id/src/lib.rs index 457188e15b1..07a867e3427 100644 --- a/core/account-id/src/lib.rs +++ b/core/account-id/src/lib.rs @@ -64,10 +64,14 @@ pub use errors::{ParseAccountError, ParseErrorKind}; #[derive(Eq, Ord, Hash, Clone, Debug, PartialEq, PartialOrd)] pub struct AccountId(Box); +/// Enum representing possible types of accounts. #[derive(PartialEq)] pub enum AccountType { + /// Any valid account, that is neither NEAR-implicit nor ETH-implicit. NamedAccount, + /// An account with 64 characters long hexadecimal address. NearImplicitAccount, + /// An account which address starts with '0x', followed by 40 hex characters. EthImplicitAccount, } @@ -75,7 +79,7 @@ impl AccountType { pub fn is_implicit(&self) -> bool { match &self { Self::NearImplicitAccount => true, - // TODO change to true later, see https://github.com/near/nearcore/issues/10018 + // TODO(eth-implicit) change to true later, see https://github.com/near/nearcore/issues/10018 Self::EthImplicitAccount => false, Self::NamedAccount => false, } @@ -150,7 +154,7 @@ impl AccountId { .map_or(false, |s| !s.contains('.')) } - /// Returns `true` if the `AccountId` is a 40 characters long hexadecimal prefixed with '0x'. + /// Returns `true` if the `AccountId` is '0x' followed by 40 hex characters (42 characters in total). /// /// See [Implicit-Accounts](https://docs.near.org/docs/concepts/account#implicit-accounts). /// @@ -475,7 +479,9 @@ mod tests { "10-4.8-2", "b-o_w_e-n", "no_lols", + // NEAR-implicit account "0123456789012345678901234567890123456789012345678901234567890123", + // ETH-implicit account "0xb794f5ea0ba39494ce839613fffba74279579268", // Valid, but can't be created "near.a", @@ -592,7 +598,9 @@ mod tests { "alex-skidanov", "b-o_w_e-n", "no_lols", + // ETH-implicit account "0xb794f5ea0ba39494ce839613fffba74279579268", + // NEAR-implicit account "0123456789012345678901234567890123456789012345678901234567890123", ]; for account_id in ok_top_level_account_ids { @@ -719,6 +727,7 @@ mod tests { ), ( "b794f5ea0ba39494ce839613fffba74279579268", + // ETH-implicit account "0xb794f5ea0ba39494ce839613fffba74279579268", ), ("aa", "ъ@aa"), @@ -764,6 +773,7 @@ mod tests { "fffff_ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", "00000000000000000000000000000000000000000000000000000000000000", + "0xb794f5ea0ba39494ce839613fffba74279579268", ]; for invalid_account_id in invalid_near_implicit_account_ids { assert!( @@ -805,6 +815,7 @@ mod tests { "0xfffff_ffffffffffffffffffffffffffffffffff", "0xoooooooooooooooooooooooooooooooooooooooo", "0x00000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", ]; for invalid_account_id in invalid_eth_implicit_account_ids { assert!( diff --git a/core/primitives-core/src/runtime/fees.rs b/core/primitives-core/src/runtime/fees.rs index aa5e245107a..3b7bf5fb819 100644 --- a/core/primitives-core/src/runtime/fees.rs +++ b/core/primitives-core/src/runtime/fees.rs @@ -208,31 +208,53 @@ impl StorageUsageConfig { /// We can assume that no overflow will happen here. pub fn transfer_exec_fee( cfg: &RuntimeFeesConfig, - is_receiver_implicit: bool, + implicit_account_creation_allowed: bool, receiver_account_type: AccountType, ) -> Gas { - let mut result = cfg.fee(ActionCosts::transfer).exec_fee(); - if is_receiver_implicit { - result += cfg.fee(ActionCosts::create_account).exec_fee(); - if receiver_account_type == AccountType::NearImplicitAccount { - result += cfg.fee(ActionCosts::add_full_access_key).exec_fee(); + let implicit_account_creation = + implicit_account_creation_allowed && receiver_account_type.is_implicit(); + if !implicit_account_creation { + // No account will be created, just a regular transfer. + cfg.fee(ActionCosts::transfer).exec_fee() + } else { + match receiver_account_type { + // Extra fees for the CreateAccount and AddFullAccessKey. + AccountType::NearImplicitAccount => { + cfg.fee(ActionCosts::transfer).exec_fee() + + cfg.fee(ActionCosts::create_account).exec_fee() + + cfg.fee(ActionCosts::add_full_access_key).exec_fee() + } + // These arms are impossible, as we checked for `is_implicit` above. + AccountType::EthImplicitAccount | AccountType::NamedAccount => { + panic!("must be near-implicit") + } } } - result } pub fn transfer_send_fee( cfg: &RuntimeFeesConfig, sender_is_receiver: bool, - is_receiver_implicit: bool, + implicit_account_creation_allowed: bool, receiver_account_type: AccountType, ) -> Gas { - let mut result = cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver); - if is_receiver_implicit { - result += cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver); - if receiver_account_type == AccountType::NearImplicitAccount { - result += cfg.fee(ActionCosts::add_full_access_key).send_fee(sender_is_receiver); + let implicit_account_creation = + implicit_account_creation_allowed && receiver_account_type.is_implicit(); + if !implicit_account_creation { + // No account will be created, just a regular transfer. + cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver) + } else { + match receiver_account_type { + // Extra fees for the CreateAccount and AddFullAccessKey. + AccountType::NearImplicitAccount => { + cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver) + + cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver) + + cfg.fee(ActionCosts::add_full_access_key).send_fee(sender_is_receiver) + } + // These arms are impossible, as we checked for `is_implicit` above. + AccountType::EthImplicitAccount | AccountType::NamedAccount => { + panic!("must be near-implicit") + } } } - result } diff --git a/core/primitives/src/utils.rs b/core/primitives/src/utils.rs index 0dec0b1ef6b..06894bb9eaf 100644 --- a/core/primitives/src/utils.rs +++ b/core/primitives/src/utils.rs @@ -17,7 +17,7 @@ use crate::version::{ CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION, }; -use near_crypto::{KeyType, PublicKey}; +use near_crypto::ED25519PublicKey; use near_primitives_core::account::id::AccountId; use std::mem::size_of; @@ -471,23 +471,22 @@ where /// Derives `AccountId` from `PublicKey``. /// If the key type is ED25519, returns hex-encoded copy of the key. -pub fn derive_near_implicit_account_id(public_key: &PublicKey) -> AccountId { - match public_key.key_type() { - KeyType::ED25519 => hex::encode(public_key.key_data()).parse().unwrap(), - _ => unimplemented!(), - } +pub fn derive_near_implicit_account_id(public_key: &ED25519PublicKey) -> AccountId { + hex::encode(public_key).parse().unwrap() } #[cfg(test)] mod tests { use super::*; + use near_crypto::{KeyType, PublicKey}; #[test] fn test_derive_account_id_from_ed25519_public_key() { let public_key = PublicKey::from_seed(KeyType::ED25519, "test"); let expected: AccountId = "bb4dc639b212e075a751685b26bdcea5920a504181ff2910e8549742127092a0".parse().unwrap(); - assert_eq!(derive_near_implicit_account_id(&public_key), expected); + let account_id = derive_near_implicit_account_id(public_key.unwrap_as_ed25519()); + assert_eq!(account_id, expected); } #[test] 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 101b32e198e..bb07c943c2b 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 @@ -203,7 +203,8 @@ fn get_status_of_tx_hash_collision_for_implicit_account( fn test_transaction_hash_collision_for_implicit_account_fail() { let protocol_version = ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version(); let secret_key = SecretKey::from_seed(KeyType::ED25519, "test"); - let implicit_account_id = derive_near_implicit_account_id(&secret_key.public_key()); + let implicit_account_id = + derive_near_implicit_account_id(secret_key.public_key().unwrap_as_ed25519()); let implicit_account_signer = InMemorySigner::from_secret_key(implicit_account_id, secret_key); assert_matches!( get_status_of_tx_hash_collision_for_implicit_account( @@ -220,7 +221,8 @@ fn test_transaction_hash_collision_for_implicit_account_ok() { let protocol_version = ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version() - 1; let secret_key = SecretKey::from_seed(KeyType::ED25519, "test"); - let implicit_account_id = derive_near_implicit_account_id(&secret_key.public_key()); + let implicit_account_id = + derive_near_implicit_account_id(secret_key.public_key().unwrap_as_ed25519()); let implicit_account_signer = InMemorySigner::from_secret_key(implicit_account_id, secret_key); assert_matches!( get_status_of_tx_hash_collision_for_implicit_account( diff --git a/integration-tests/src/tests/client/features/delegate_action.rs b/integration-tests/src/tests/client/features/delegate_action.rs index 42420fcac6f..c95e7381bec 100644 --- a/integration-tests/src/tests/client/features/delegate_action.rs +++ b/integration-tests/src/tests/client/features/delegate_action.rs @@ -860,8 +860,8 @@ fn meta_tx_create_implicit_account(new_account: AccountId) { let tx_cost = match new_account.get_account_type() { AccountType::NearImplicitAccount => fee_helper.create_account_transfer_full_key_cost(), - AccountType::EthImplicitAccount => panic!("must be implicit"), - AccountType::NamedAccount => panic!("must be implicit"), + AccountType::EthImplicitAccount => panic!("must be near-implicit"), + AccountType::NamedAccount => panic!("must be near-implicit"), }; check_meta_tx_no_fn_call( &node, diff --git a/integration-tests/src/tests/standard_cases/mod.rs b/integration-tests/src/tests/standard_cases/mod.rs index fe67f4cfbeb..4a47ec2c397 100644 --- a/integration-tests/src/tests/standard_cases/mod.rs +++ b/integration-tests/src/tests/standard_cases/mod.rs @@ -336,12 +336,12 @@ pub fn transfer_tokens_implicit_account(node: impl Node, public_key: PublicKey) let root = node_user.get_state_root(); let tokens_used = 10u128.pow(25); let fee_helper = fee_helper(&node); - let receiver_id = derive_near_implicit_account_id(&public_key); + let receiver_id = derive_near_implicit_account_id(public_key.unwrap_as_ed25519()); let transfer_cost = match receiver_id.get_account_type() { AccountType::NearImplicitAccount => fee_helper.create_account_transfer_full_key_cost(), - AccountType::EthImplicitAccount => std::panic!("must be implicit"), - AccountType::NamedAccount => std::panic!("must be implicit"), + AccountType::EthImplicitAccount => std::panic!("must be near-implicit"), + AccountType::NamedAccount => std::panic!("must be near-implicit"), }; let transaction_result = @@ -369,8 +369,8 @@ pub fn transfer_tokens_implicit_account(node: impl Node, public_key: PublicKey) AccountType::NearImplicitAccount => { assert_eq!(view_access_key.unwrap(), AccessKey::full_access().into()); } - AccountType::EthImplicitAccount => std::panic!("must be implicit"), - AccountType::NamedAccount => std::panic!("must be implicit"), + AccountType::EthImplicitAccount => std::panic!("must be near-implicit"), + AccountType::NamedAccount => std::panic!("must be near-implicit"), } let transaction_result = @@ -401,7 +401,7 @@ pub fn trying_to_create_implicit_account(node: impl Node, public_key: PublicKey) let root = node_user.get_state_root(); let tokens_used = 10u128.pow(25); let fee_helper = fee_helper(&node); - let receiver_id = derive_near_implicit_account_id(&public_key); + let receiver_id = derive_near_implicit_account_id(public_key.unwrap_as_ed25519()); let transaction_result = node_user .create_account( @@ -414,17 +414,18 @@ pub fn trying_to_create_implicit_account(node: impl Node, public_key: PublicKey) let cost = match receiver_id.get_account_type() { AccountType::NearImplicitAccount => { - fee_helper.create_account_transfer_full_key_cost_fail_on_create_account() - + fee_helper.gas_to_balance( - fee_helper.cfg().fee(ActionCosts::create_account).send_fee(false) - + fee_helper - .cfg() - .fee(near_primitives::config::ActionCosts::add_full_access_key) - .send_fee(false), - ) + let fail_cost = + fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(); + let create_account_fee = + fee_helper.cfg().fee(ActionCosts::create_account).send_fee(false); + let add_access_key_fee = fee_helper + .cfg() + .fee(near_primitives::config::ActionCosts::add_full_access_key) + .send_fee(false); + fail_cost + fee_helper.gas_to_balance(create_account_fee + add_access_key_fee) } - AccountType::EthImplicitAccount => std::panic!("must be implicit"), - AccountType::NamedAccount => std::panic!("must be implicit"), + AccountType::EthImplicitAccount => std::panic!("must be near-implicit"), + AccountType::NamedAccount => std::panic!("must be near-implicit"), }; assert_eq!( diff --git a/runtime/near-vm-runner/src/logic/logic.rs b/runtime/near-vm-runner/src/logic/logic.rs index 9e02b6578fe..16804396580 100644 --- a/runtime/near-vm-runner/src/logic/logic.rs +++ b/runtime/near-vm-runner/src/logic/logic.rs @@ -1773,17 +1773,15 @@ impl<'a> VMLogic<'a> { let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; let receiver_id = self.ext.get_receipt_receiver(receipt_idx); - let is_receiver_implicit = - self.config.implicit_account_creation && receiver_id.get_account_type().is_implicit(); let send_fee = transfer_send_fee( self.fees_config, sir, - is_receiver_implicit, + self.config.implicit_account_creation, receiver_id.get_account_type(), ); let exec_fee = transfer_exec_fee( self.fees_config, - is_receiver_implicit, + self.config.implicit_account_creation, receiver_id.get_account_type(), ); let burn_gas = send_fee; diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 433906c74ee..cc4b60eceae 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -429,6 +429,7 @@ pub(crate) fn action_create_account( )); } +/// Can only be used for NEAR-implicit accounts. pub(crate) fn action_implicit_account_creation_transfer( state_update: &mut TrieUpdate, fee_config: &RuntimeFeesConfig, @@ -472,7 +473,9 @@ 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 | AccountType::NamedAccount => panic!("must be implicit"), + AccountType::EthImplicitAccount | AccountType::NamedAccount => { + panic!("must be near-implicit") + } } } diff --git a/runtime/runtime/src/config.rs b/runtime/runtime/src/config.rs index e5abb47e9c4..37d9e3627e5 100644 --- a/runtime/runtime/src/config.rs +++ b/runtime/runtime/src/config.rs @@ -96,14 +96,11 @@ pub fn total_send_fees( } Transfer(_) => { // Account for implicit account creation - let receiver_account_type = receiver_id.get_account_type(); - let is_receiver_implicit = config.wasm_config.implicit_account_creation - && receiver_account_type.is_implicit(); transfer_send_fee( fees, sender_is_receiver, - is_receiver_implicit, - receiver_account_type, + config.wasm_config.implicit_account_creation, + receiver_id.get_account_type(), ) } Stake(_) => fees.fee(ActionCosts::stake).send_fee(sender_is_receiver), @@ -194,10 +191,11 @@ pub fn exec_fee(config: &RuntimeConfig, action: &Action, receiver_id: &AccountId } Transfer(_) => { // Account for implicit account creation - let receiver_account_type = receiver_id.get_account_type(); - let is_receiver_implicit = - config.wasm_config.implicit_account_creation && receiver_account_type.is_implicit(); - transfer_exec_fee(fees, is_receiver_implicit, receiver_account_type) + transfer_exec_fee( + fees, + config.wasm_config.implicit_account_creation, + receiver_id.get_account_type(), + ) } Stake(_) => fees.fee(ActionCosts::stake).exec_fee(), AddKey(add_key_action) => match &add_key_action.access_key.permission { diff --git a/tools/mirror/src/key_mapping.rs b/tools/mirror/src/key_mapping.rs index 1f2b1211e30..4cbc8608419 100644 --- a/tools/mirror/src/key_mapping.rs +++ b/tools/mirror/src/key_mapping.rs @@ -103,9 +103,9 @@ pub fn map_account( match account_id.get_account_type() { AccountType::NearImplicitAccount => { let public_key = - PublicKey::from_near_implicit_account(account_id).expect("must be implicit"); + PublicKey::from_near_implicit_account(account_id).expect("must be near-implicit"); let mapped_key = map_key(&public_key, secret); - derive_near_implicit_account_id(&mapped_key.public_key()) + derive_near_implicit_account_id(mapped_key.public_key().unwrap_as_ed25519()) } AccountType::EthImplicitAccount => account_id.clone(), AccountType::NamedAccount => account_id.clone(), diff --git a/tools/mirror/src/lib.rs b/tools/mirror/src/lib.rs index 6f829c2161f..c09a4b75b33 100644 --- a/tools/mirror/src/lib.rs +++ b/tools/mirror/src/lib.rs @@ -1008,7 +1008,7 @@ impl TxMirror { { let public_key = PublicKey::from_near_implicit_account(&target_account) - .expect("must be implicit"); + .expect("must be near-implicit"); nonce_updates.insert((target_account, public_key)); } }