Skip to content

Commit

Permalink
Mark implicit-account related code
Browse files Browse the repository at this point in the history
  • Loading branch information
staffik committed Oct 19, 2023
1 parent 656c7f5 commit 3981e27
Show file tree
Hide file tree
Showing 23 changed files with 164 additions and 85 deletions.
1 change: 1 addition & 0 deletions chain/rosetta-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ async fn construction_derive(
let public_key: near_crypto::PublicKey = (&public_key)
.try_into()
.map_err(|_| errors::ErrorKind::InvalidInput("Invalid PublicKey".to_string()))?;
// TODO handle eth-implicit accounts
let address = if let near_crypto::KeyType::ED25519 = public_key.key_type() {
hex::encode(public_key.key_data())
} else {
Expand Down
43 changes: 37 additions & 6 deletions core/account-id/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,35 @@ impl AccountId {
/// assert!(!alice_app.is_sub_account_of(&near_tla));
/// ```
pub fn is_sub_account_of(&self, parent: &AccountId) -> bool {
// TODO must be !self.is_ximplicit() ?
self.strip_suffix(parent.as_str())
.and_then(|s| s.strip_suffix('.'))
.map_or(false, |s| !s.contains('.'))
}

// TODO change docs
/// Returns `true` if the `AccountId` is a 40 characters long hexadecimal, possibly with capital letters.
///
/// See [Implicit-Accounts](https://docs.near.org/docs/concepts/account#implicit-accounts).
///
/// ## Examples
///
/// ```
/// use near_account_id::AccountId;
///
/// let alice: AccountId = "alice.near".parse().unwrap();
/// assert!(!alice.is_eth());
///
/// let rando = "b794f5eA0ba39494ce839613FFfba74279579268"
/// .parse::<AccountId>()
/// .unwrap();
/// assert!(rando.is_eth());
/// ```
pub fn is_eth(&self) -> bool {
self.len() == 40
&& self.as_bytes().iter().all(|b| matches!(b, b'a'..=b'f' | b'A'..=b'F' | b'0'..=b'9'))
}

/// Returns `true` if the `AccountId` is a 64 characters long hexadecimal.
///
/// See [Implicit-Accounts](https://docs.near.org/docs/concepts/account#implicit-accounts).
Expand All @@ -142,17 +166,22 @@ impl AccountId {
/// use near_account_id::AccountId;
///
/// let alice: AccountId = "alice.near".parse().unwrap();
/// assert!(!alice.is_implicit());
/// assert!(!alice.is_near_implicit());
///
/// let rando = "98793cd91a3f870fb126f66285808c7e094afcfc4eda8a970f6648cdf0dbd6de"
/// .parse::<AccountId>()
/// .unwrap();
/// assert!(rando.is_implicit());
/// assert!(rando.is_near_implicit());
/// ```
pub fn is_implicit(&self) -> bool {
pub fn is_near_implicit(&self) -> bool {
self.len() == 64 && self.as_bytes().iter().all(|b| matches!(b, b'a'..=b'f' | b'0'..=b'9'))
}

/// Returns `true` if this `AccountId` is either NEAR implicit address or ETH-format address.
pub fn is_ximplicit(&self) -> bool {
self.is_near_implicit() || self.is_eth()
}

/// Returns `true` if this `AccountId` is the system account.
///
/// See [System account](https://nomicon.io/DataStructures/Account.html?highlight=system#system-account).
Expand Down Expand Up @@ -401,7 +430,7 @@ impl<'a> arbitrary::Arbitrary<'a> for AccountId {
mod tests {
use super::*;

pub const OK_ACCOUNT_IDS: [&str; 24] = [
pub const OK_ACCOUNT_IDS: [&str; 25] = [
"aa",
"a-a",
"a-aa",
Expand All @@ -425,6 +454,7 @@ mod tests {
"b-o_w_e-n",
"no_lols",
"0123456789012345678901234567890123456789012345678901234567890123",
"b794f5eA0ba39494ce839613FFfba74279579268",
// Valid, but can't be created
"near.a",
];
Expand Down Expand Up @@ -680,6 +710,7 @@ mod tests {
}
}

// TODO add corresponding test for 40 len hex ETH accounts
#[test]
fn test_is_account_id_64_len_hex() {
let valid_64_len_hex_account_ids = &[
Expand All @@ -693,7 +724,7 @@ mod tests {
assert!(
matches!(
valid_account_id.parse::<AccountId>(),
Ok(account_id) if account_id.is_implicit()
Ok(account_id) if account_id.is_near_implicit()
),
"Account ID {} should be valid 64-len hex",
valid_account_id
Expand All @@ -712,7 +743,7 @@ mod tests {
assert!(
!matches!(
invalid_account_id.parse::<AccountId>(),
Ok(account_id) if account_id.is_implicit()
Ok(account_id) if account_id.is_near_implicit()
),
"Account ID {} is not an implicit account",
invalid_account_id
Expand Down
4 changes: 2 additions & 2 deletions core/crypto/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ impl From<ParseKeyTypeError> for ParseSignatureError {

#[derive(Debug, Clone, thiserror::Error)]
pub enum ImplicitPublicKeyError {
#[error("'{account_id}' is not an implicit account")]
AccountIsNotImplicit { account_id: AccountId },
#[error("'{account_id}' is not a NEAR-implicit account")]
AccountIsNotNearImplicit { account_id: AccountId },
}
14 changes: 7 additions & 7 deletions core/crypto/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,16 @@ impl<
}

impl PublicKey {
/// Create the implicit public key from an implicit account ID.
/// Create the implicit public key from an NEAR-implicit account ID.
///
/// Returns `ImplicitPublicKeyError::AccountIsNotImplicit` if the given
/// account id is not a valid implicit account ID.
/// See [`near_account_id::AccountId#is_implicit`] for the definition.
pub fn from_implicit_account(
/// Returns `ImplicitPublicKeyError::AccountIsNotNearImplicit` if the given
/// account id is not a valid NEAR-implicit account ID.
/// See [`near_account_id::AccountId#is_near_implicit`] for the definition.
pub fn from_near_implicit_account(
account_id: &near_account_id::AccountId,
) -> Result<Self, ImplicitPublicKeyError> {
if !account_id.is_implicit() {
return Err(ImplicitPublicKeyError::AccountIsNotImplicit {
if !account_id.is_near_implicit() {
return Err(ImplicitPublicKeyError::AccountIsNotNearImplicit {
account_id: account_id.clone(),
});
}
Expand Down
31 changes: 19 additions & 12 deletions core/primitives-core/src/runtime/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,28 +203,35 @@ impl StorageUsageConfig {

/// Helper functions for computing Transfer fees.
/// In case of implicit account creation they always include extra fees for the CreateAccount and
/// AddFullAccessKey actions that are implicit.
/// AddFullAccessKey (except ETH-implicit account) actions that are implicit.
/// We can assume that no overflow will happen here.
pub fn transfer_exec_fee(cfg: &RuntimeFeesConfig, is_receiver_implicit: bool) -> Gas {
pub fn transfer_exec_fee(
cfg: &RuntimeFeesConfig,
is_receiver_implicit: bool,
is_receiver_eth: bool,
) -> Gas {
let mut result = cfg.fee(ActionCosts::transfer).exec_fee();
if is_receiver_implicit {
cfg.fee(ActionCosts::create_account).exec_fee()
+ cfg.fee(ActionCosts::add_full_access_key).exec_fee()
+ cfg.fee(ActionCosts::transfer).exec_fee()
} else {
cfg.fee(ActionCosts::transfer).exec_fee()
result += cfg.fee(ActionCosts::create_account).exec_fee();
if !is_receiver_eth {
result += cfg.fee(ActionCosts::add_full_access_key).exec_fee();
}
}
result
}

pub fn transfer_send_fee(
cfg: &RuntimeFeesConfig,
sender_is_receiver: bool,
is_receiver_implicit: bool,
is_receiver_eth: bool,
) -> Gas {
let mut result = cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver);
if is_receiver_implicit {
cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver)
+ cfg.fee(ActionCosts::add_full_access_key).send_fee(sender_is_receiver)
+ cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver)
} else {
cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver)
result += cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver);
if !is_receiver_eth {
result += cfg.fee(ActionCosts::add_full_access_key).send_fee(sender_is_receiver);
}
}
result
}
4 changes: 3 additions & 1 deletion core/primitives/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ pub enum InvalidTxError {
ActionsValidation(ActionsValidationError),
/// The size of serialized transaction exceeded the limit.
TransactionSizeExceeded { size: u64, limit: u64 },
/// Transaction signer ID is ETH-implicit but not equal to keccak(pk)[:20].
InvalidEthImplicitSignerId { signer_id: AccountId, public_key: PublicKey },
}

impl std::error::Error for InvalidTxError {}
Expand Down Expand Up @@ -485,7 +487,7 @@ pub enum ActionErrorKind {
/// receipt validation.
NewReceiptValidationError(ReceiptValidationError),
/// Error occurs when a `CreateAccount` action is called on hex-characters
/// account of length 64. See implicit account creation NEP:
/// account of length 64 or 40. See implicit account creation NEP:
/// <https://github.com/nearprotocol/NEPs/pull/71>.
///
/// TODO(#8598): This error is named very poorly. A better name would be
Expand Down
7 changes: 5 additions & 2 deletions core/primitives/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,15 +555,18 @@ pub fn create_test_signer(account_name: &str) -> InMemoryValidatorSigner {
/// Should be used only in tests.
pub fn create_user_test_signer(account_name: &str) -> InMemorySigner {
let account_id = account_name.parse().unwrap();
if account_id == implicit_test_account() {
// TODO add support for ETH-implicit test account
if account_id == near_implicit_test_account() {
InMemorySigner::from_secret_key(account_id, implicit_test_account_secret())
} else {
InMemorySigner::from_seed(account_id, KeyType::ED25519, account_name)
}
}

// TODO add eth_implicit_test_account

/// A fixed implicit account for which tests can know the private key.
pub fn implicit_test_account() -> AccountId {
pub fn near_implicit_test_account() -> AccountId {
"061b1dd17603213b00e1a1e53ba060ad427cef4887bd34a5e0ef09010af23b0a".parse().unwrap()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,12 @@ fn test_transaction_hash_collision() {
);
}

// TODO add corresponding method for ETH-implicit accounts?
/// Helper for checking that duplicate transactions from implicit accounts are properly rejected.
/// It creates implicit account, deletes it and creates again, so that nonce of the access
/// key is updated. Then it tries to send tx from implicit account with invalid nonce, which
/// should fail since the protocol upgrade.
fn get_status_of_tx_hash_collision_for_implicit_account(
fn get_status_of_tx_hash_collision_for_near_implicit_account(
protocol_version: ProtocolVersion,
) -> ProcessTxResponse {
let epoch_length = 100;
Expand Down Expand Up @@ -202,23 +203,25 @@ fn get_status_of_tx_hash_collision_for_implicit_account(
response
}

// TODO add corresponding test for ETH-implicit accounts?
/// Test that duplicate transactions from implicit accounts are properly rejected.
#[test]
fn test_transaction_hash_collision_for_implicit_account_fail() {
fn test_transaction_hash_collision_for_near_implicit_account_fail() {
let protocol_version = ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version();
assert_matches!(
get_status_of_tx_hash_collision_for_implicit_account(protocol_version),
get_status_of_tx_hash_collision_for_near_implicit_account(protocol_version),
ProcessTxResponse::InvalidTx(InvalidTxError::InvalidNonce { .. })
);
}

// TODO add corresponding test for ETH-implicit accounts?
/// Test that duplicate transactions from implicit accounts are not rejected until protocol upgrade.
#[test]
fn test_transaction_hash_collision_for_implicit_account_ok() {
fn test_transaction_hash_collision_for_near_implicit_account_ok() {
let protocol_version =
ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version() - 1;
assert_matches!(
get_status_of_tx_hash_collision_for_implicit_account(protocol_version),
get_status_of_tx_hash_collision_for_near_implicit_account(protocol_version),
ProcessTxResponse::ValidTx
);
}
Expand Down
26 changes: 16 additions & 10 deletions integration-tests/src/tests/client/features/delegate_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use near_primitives::errors::{
ActionError, ActionErrorKind, ActionsValidationError, InvalidAccessKeyError, InvalidTxError,
TxExecutionError,
};
use near_primitives::test_utils::{create_user_test_signer, implicit_test_account};
use near_primitives::test_utils::{create_user_test_signer, near_implicit_test_account};
use near_primitives::transaction::{
Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction,
DeployContractAction, FunctionCallAction, StakeAction, TransferAction,
Expand Down Expand Up @@ -133,8 +133,11 @@ fn check_meta_tx_execution(
.get_access_key(&relayer, &PublicKey::from_seed(KeyType::ED25519, &relayer))
.unwrap()
.nonce;
let user_pubk = if sender.is_implicit() {
PublicKey::from_implicit_account(&sender).unwrap()
let user_pubk = if sender.is_near_implicit() {
PublicKey::from_near_implicit_account(&sender).unwrap()
} else if sender.is_eth() {
// TODO
panic!("check_meta_tx_execution for eth sender address");
} else {
PublicKey::from_seed(KeyType::ED25519, &sender)
};
Expand Down Expand Up @@ -777,13 +780,14 @@ fn meta_tx_create_named_account() {
node.view_account(&new_account).expect("failed looking up account");
}

/// Try creating an implicit account with `CreateAction` which is not allowed in
// TODO add corresponding test for ETH-implicit account
/// Try creating a NEAR-implicit account with `CreateAction` which is not allowed in
/// or outside meta transactions and must fail with `OnlyImplicitAccountCreationAllowed`.
#[test]
fn meta_tx_create_implicit_account_fails() {
fn meta_tx_create_near_implicit_account_fails() {
let relayer = bob_account();
let sender = alice_account();
let new_account: AccountId = implicit_test_account();
let new_account: AccountId = near_implicit_test_account();
let node = RuntimeNode::new(&relayer);

let actions = vec![Action::CreateAccount(CreateAccountAction {})];
Expand All @@ -798,6 +802,7 @@ fn meta_tx_create_implicit_account_fails() {
));
}

// TODO add corresponding test for ETH-implicit account
/// Try creating an implicit account with a meta tx transfer and use the account
/// in the same meta transaction.
///
Expand All @@ -808,7 +813,7 @@ fn meta_tx_create_implicit_account_fails() {
fn meta_tx_create_and_use_implicit_account() {
let relayer = bob_account();
let sender = alice_account();
let new_account: AccountId = implicit_test_account();
let new_account: AccountId = near_implicit_test_account();
let node = RuntimeNode::new(&relayer);

// Check the account doesn't exist, yet. We will attempt creating it.
Expand All @@ -832,17 +837,18 @@ fn meta_tx_create_and_use_implicit_account() {
));
}

/// Creating an implicit account with a meta tx transfer and use the account in
// TODO add corresponding test for ETH-implicit account
/// Creating a NEAR-implicit account with a meta tx transfer and use the account in
/// a second meta transaction.
///
/// Creation through a meta tx should work as normal, it's just that the relayer
/// pays for the storage and the user could delete the account and cash in,
/// hence this workflow is not ideal from all circumstances.
#[test]
fn meta_tx_create_implicit_account() {
fn meta_tx_create_near_implicit_account() {
let relayer = bob_account();
let sender = alice_account();
let new_account: AccountId = implicit_test_account();
let new_account: AccountId = near_implicit_test_account();
let node = RuntimeNode::new(&relayer);

// Check account doesn't exist, yet
Expand Down
8 changes: 4 additions & 4 deletions integration-tests/src/tests/client/features/restrict_tla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ fn test_create_top_level_accounts() {
.build();

// These accounts cannot be created because they are top level accounts that are not implicit.
// Note that implicit accounts have to be 64 characters long.
// Note that implicit accounts have to be 64 or 40 characters long.
let top_level_accounts = [
"0x06012c8cf97bead5deae237070f9587f8e7a266d",
"0x5e97870f263700f46aa00d967821199b9bc5a120",
"0x0000000000000000000000000000000000000000",
"0x06012c8cf97bead5deae237070f9587f8e7a266da",
"0a5e97870f263700f46aa00d967821199b9bc5a120",
"0x000000000000000000000000000000000000000",
"alice",
"thisisaveryverylongtoplevelaccount",
];
Expand Down
6 changes: 4 additions & 2 deletions integration-tests/src/tests/standard_cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,8 @@ pub fn test_send_money(node: impl Node) {
);
}

pub fn transfer_tokens_implicit_account(node: impl Node) {
// TODO add coresponding method for ETH-implicit account
pub fn transfer_tokens_near_implicit_account(node: impl Node) {
let account_id = &node.account_id().unwrap();
let node_user = node.user();
let root = node_user.get_state_root();
Expand Down Expand Up @@ -382,7 +383,8 @@ pub fn transfer_tokens_implicit_account(node: impl Node) {
assert_eq!((amount, locked), (tokens_used * 2, 0));
}

pub fn trying_to_create_implicit_account(node: impl Node) {
// TODO add coresponding method for ETH-implicit account
pub fn trying_to_create_near_implicit_account(node: impl Node) {
let account_id = &node.account_id().unwrap();
let node_user = node.user();
let root = node_user.get_state_root();
Expand Down
Loading

0 comments on commit 3981e27

Please sign in to comment.