Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: set access key nonce on implicit account creation #5482

Merged
merged 25 commits into from
Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7f7347f
implement test
Looogarithm Nov 26, 2021
2ff38bf
fix comment
Looogarithm Nov 26, 2021
218331f
Merge branch 'master' into fix-access-key
Longarithm Nov 26, 2021
291d052
Merge branch 'master' into fix-access-key
Longarithm Nov 26, 2021
4c9b20f
move to nightly version
Looogarithm Nov 26, 2021
c029d42
Merge branch 'fix-access-key' of github.com:near/nearcore into fix-ac…
Looogarithm Nov 26, 2021
947d59b
compilation fix
Looogarithm Nov 26, 2021
e600a79
comments
Looogarithm Nov 26, 2021
218666d
Merge branch 'master' into fix-access-key
Longarithm Nov 26, 2021
a033226
feature fixes
Looogarithm Nov 26, 2021
ed719f6
Merge branch 'fix-access-key' of github.com:near/nearcore into fix-ac…
Looogarithm Nov 26, 2021
7f35a31
apply suggestions
Looogarithm Nov 26, 2021
32290bb
Merge branch 'master' into fix-access-key
Longarithm Nov 26, 2021
b61e595
apply suggestions
Looogarithm Nov 26, 2021
48d71fd
fix hashes
Looogarithm Nov 26, 2021
5c4f39d
fix hashes
Looogarithm Nov 27, 2021
6c254dd
fix version
Looogarithm Nov 27, 2021
79e7273
fix hashes
Looogarithm Nov 29, 2021
8c1f46d
Merge branch 'master' into fix-access-key
Longarithm Nov 29, 2021
fac4d29
apply new suggestions
Looogarithm Nov 29, 2021
aca7b50
Merge branch 'master' into fix-access-key
Looogarithm Nov 29, 2021
94a2d63
Merge refs/heads/master into fix-access-key
near-bulldozer[bot] Nov 29, 2021
4f37a07
Merge refs/heads/master into fix-access-key
near-bulldozer[bot] Nov 29, 2021
195003c
Merge refs/heads/master into fix-access-key
near-bulldozer[bot] Nov 29, 2021
23ce0e8
Merge refs/heads/master into fix-access-key
near-bulldozer[bot] Nov 29, 2021
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
4 changes: 2 additions & 2 deletions chain/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ expensive_tests = []
test_features = []
delay_detector = ["delay-detector"]
no_cache = ["near-store/no_cache"]
protocol_feature_chunk_only_producers = ["near-primitives/protocol_feature_chunk_only_producers"]
protocol_feature_chunk_only_producers = ["near-chain-configs/protocol_feature_chunk_only_producers", "near-primitives/protocol_feature_chunk_only_producers"]

protocol_feature_routing_exchange_algorithm = []
nightly_protocol_features = ["nightly_protocol", "protocol_feature_chunk_only_producers", "protocol_feature_routing_exchange_algorithm"]
nightly_protocol = []
nightly_protocol = ["near-store/nightly_protocol", "near-primitives/nightly_protocol"]
Longarithm marked this conversation as resolved.
Show resolved Hide resolved
sandbox = []
6 changes: 3 additions & 3 deletions chain/chain/src/tests/simple_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn empty_chain() {
assert_eq!(chain.head().unwrap().height, 0);
let hash = chain.head().unwrap().last_block_hash;
#[cfg(feature = "nightly_protocol")]
assert_eq!(hash, CryptoHash::from_str("DHFJfDH25yEPmEpKrUN9upXY3gFcCmsGyHMTk2sugyXd").unwrap());
assert_eq!(hash, CryptoHash::from_str("3wFoFbJPPv7Jg1ZERuBWTCok4H9uRZ7Uagm1wiXdKpvV").unwrap());
#[cfg(not(feature = "nightly_protocol"))]
assert_eq!(hash, CryptoHash::from_str("BkrZeGCDnYJGAdc3G1fb1P8FMbnNDhBn6w5DjhQeNAdp").unwrap());
assert_eq!(count_utc, 1);
Expand All @@ -44,7 +44,7 @@ fn build_chain() {
#[cfg(feature = "nightly_protocol")]
assert_eq!(
prev_hash,
CryptoHash::from_str("FrAMjjHhjJAB1N6BiDVpRfAMsjLHbTYZnkYKUs39JgEx").unwrap()
CryptoHash::from_str("5VroU43sRYCkttuJfpdP6iC57fsD4q2TCbWaronBWpz7").unwrap()
);
#[cfg(not(feature = "nightly_protocol"))]
assert_eq!(
Expand All @@ -67,7 +67,7 @@ fn build_chain() {
#[cfg(feature = "nightly_protocol")]
assert_eq!(
chain.head().unwrap().last_block_hash,
CryptoHash::from_str("2zngdBZU9YEZKvtfpkZb3vjjxf7j7szMdo5jtPLDcg5Z").unwrap()
CryptoHash::from_str("BNap11nsM7PEqYQertYU487g7gkCteQ8Fmee6QfyRdVQ").unwrap()
);
#[cfg(not(feature = "nightly_protocol"))]
assert_eq!(
Expand Down
7 changes: 3 additions & 4 deletions core/primitives-core/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,9 @@ impl BorshSerialize for Account {
BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Debug,
)]
pub struct AccessKey {
/// The nonce for this access key.
/// NOTE: In some cases the access key needs to be recreated. If the new access key reuses the
/// same public key, the nonce of the new access key should be equal to the nonce of the old
/// access key. It's required to avoid replaying old transactions again.
/// Nonce for this access key, used for tx nonce generation. When access key is created, nonce
/// is set to `(block_height - 1) * 1e6` to avoid tx hash collision on access key re-creation.
/// See <https://github.com/near/nearcore/issues/3779> for more details.
pub nonce: Nonce,

/// Defines permissions for this access key.
Expand Down
3 changes: 2 additions & 1 deletion core/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ dump_errors_schema = ["near-rpc-error-macro/dump_errors_schema"]
protocol_feature_alt_bn128 = ["near-primitives-core/protocol_feature_alt_bn128", "near-vm-errors/protocol_feature_alt_bn128"]
protocol_feature_chunk_only_producers = []
protocol_feature_routing_exchange_algorithm = ["near-primitives-core/protocol_feature_routing_exchange_algorithm"]
nightly_protocol_features = ["nightly_protocol", "protocol_feature_alt_bn128", "protocol_feature_chunk_only_producers", "protocol_feature_routing_exchange_algorithm"]
protocol_feature_access_key_nonce_for_implicit_accounts = []
nightly_protocol_features = ["nightly_protocol", "protocol_feature_alt_bn128", "protocol_feature_chunk_only_producers", "protocol_feature_routing_exchange_algorithm", "protocol_feature_access_key_nonce_for_implicit_accounts"]
nightly_protocol = []
deepsize_feature = ["deepsize", "near-vm-errors/deepsize_feature", "near-primitives-core/deepsize_feature", "near-crypto/deepsize_feature"]

Expand Down
9 changes: 8 additions & 1 deletion core/primitives/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ pub enum ProtocolFeature {
// stable features
ForwardChunkParts,
RectifyInflation,
/// Add `AccessKey` nonce range by setting nonce to `(block_height - 1) * 1e6`, see
/// <https://github.com/near/nearcore/issues/3779>.
AccessKeyNonceRange,
FixApplyChunks,
LowerStorageCost,
Expand Down Expand Up @@ -136,6 +138,9 @@ pub enum ProtocolFeature {
ChunkOnlyProducers,
#[cfg(feature = "protocol_feature_routing_exchange_algorithm")]
RoutingExchangeAlgorithm,
#[cfg(feature = "protocol_feature_access_key_nonce_for_implicit_accounts")]
/// Add `AccessKey` nonce range for implicit accounts, as in `AccessKeyNonceRange` feature.
AccessKeyNonceForImplicitAccounts,
}

/// Current latest stable version of the protocol.
Expand All @@ -146,7 +151,7 @@ pub const PROTOCOL_VERSION: ProtocolVersion = 49;

/// Current latest nightly version of the protocol.
#[cfg(feature = "nightly_protocol")]
pub const PROTOCOL_VERSION: ProtocolVersion = 124;
pub const PROTOCOL_VERSION: ProtocolVersion = 125;

impl ProtocolFeature {
pub const fn protocol_version(self) -> ProtocolVersion {
Expand Down Expand Up @@ -181,6 +186,8 @@ impl ProtocolFeature {
ProtocolFeature::ChunkOnlyProducers => 124,
#[cfg(feature = "protocol_feature_routing_exchange_algorithm")]
ProtocolFeature::RoutingExchangeAlgorithm => 117,
#[cfg(feature = "protocol_feature_access_key_nonce_for_implicit_accounts")]
ProtocolFeature::AccessKeyNonceForImplicitAccounts => 125,
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ protocol_feature_alt_bn128 = [
"near-vm-errors/protocol_feature_alt_bn128",
]
protocol_feature_chunk_only_producers = ["near-client/protocol_feature_chunk_only_producers", "near-primitives/protocol_feature_chunk_only_producers"]
nightly_protocol_features = ["nearcore/nightly_protocol_features", "protocol_feature_alt_bn128", "protocol_feature_chunk_only_producers"]
protocol_feature_access_key_nonce_for_implicit_accounts = ["near-primitives/protocol_feature_access_key_nonce_for_implicit_accounts", "node-runtime/protocol_feature_access_key_nonce_for_implicit_accounts"]
nightly_protocol_features = ["nearcore/nightly_protocol_features", "protocol_feature_alt_bn128", "protocol_feature_chunk_only_producers", "protocol_feature_access_key_nonce_for_implicit_accounts"]
nightly_protocol = ["nearcore/nightly_protocol"]
sandbox = ["near-network/sandbox", "near-chain/sandbox", "node-runtime/sandbox", "near-client/sandbox"]
no_cache = ["nearcore/no_cache"]
150 changes: 143 additions & 7 deletions integration-tests/tests/client/process_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,14 @@ pub fn create_nightshade_runtimes(genesis: &Genesis, n: usize) -> Vec<Arc<dyn Ru
.collect()
}

fn produce_epochs(
/// Produce `blocks_number` block in the given environment, starting from the given height.
/// Returns the first unoccupied height in the chain after this operation.
fn produce_blocks_from_height(
Longarithm marked this conversation as resolved.
Show resolved Hide resolved
env: &mut TestEnv,
epoch_number: u64,
epoch_length: u64,
blocks_number: u64,
height: BlockHeight,
) -> BlockHeight {
let next_height = height + epoch_number * epoch_length;
let next_height = height + blocks_number;
for i in height..next_height {
let block = env.clients[0].produce_block(i).unwrap().unwrap();
env.process_block(0, block.clone(), Provenance::PRODUCED);
Expand All @@ -112,6 +113,22 @@ fn produce_epochs(
next_height
}

/// Try to process tx in the next blocks, check that tx and all generated receipts succeed.
/// Return height of the next block.
fn check_tx_processing(
env: &mut TestEnv,
tx: SignedTransaction,
height: BlockHeight,
blocks_number: u64,
) -> BlockHeight {
let tx_hash = tx.get_hash().clone();
env.clients[0].process_tx(tx, false, false);
let next_height = produce_blocks_from_height(env, blocks_number, height);
let final_outcome = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap();
assert!(matches!(final_outcome.status, FinalExecutionStatus::SuccessValue(_)));
next_height
}

fn deploy_test_contract(
env: &mut TestEnv,
account_id: AccountId,
Expand All @@ -132,7 +149,7 @@ fn deploy_test_contract(
*block.hash(),
);
env.clients[0].process_tx(tx, false, false);
produce_epochs(env, 1, epoch_length, height)
produce_blocks_from_height(env, epoch_length, height)
}

/// Create environment and set of transactions which cause congestion on the chain.
Expand Down Expand Up @@ -3331,6 +3348,125 @@ mod access_key_nonce_range_tests {
assert!(matches!(res, NetworkClientResponses::InvalidTx(_)));
}

/// 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(
protocol_version: ProtocolVersion,
) -> NetworkClientResponses {
let epoch_length = 100;
let mut genesis =
Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1);
genesis.config.epoch_length = epoch_length;
genesis.config.protocol_version = protocol_version;
let mut env = TestEnv::builder(ChainGenesis::test())
.runtime_adapters(create_nightshade_runtimes(&genesis, 1))
.build();
let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap().clone();

let signer1 =
InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1");

let public_key = signer1.public_key.clone();
let raw_public_key = public_key.unwrap_as_ed25519().0.to_vec();
let implicit_account_id = AccountId::try_from(hex::encode(&raw_public_key)).unwrap();
let implicit_account_signer = InMemorySigner::from_secret_key(
implicit_account_id.clone(),
signer1.secret_key.clone(),
);
let deposit_for_account_creation = 10u128.pow(23);
let mut height = 1;
let blocks_number = 5;

// Send money to implicit account, invoking its creation.
let send_money_tx = SignedTransaction::send_money(
1,
"test1".parse().unwrap(),
implicit_account_id.clone(),
&signer1,
deposit_for_account_creation.clone(),
*genesis_block.hash(),
);
height = check_tx_processing(&mut env, send_money_tx, height, blocks_number.clone());
let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap().clone();

// Delete implicit account.
let delete_account_tx = SignedTransaction::delete_account(
// Because AccessKeyNonceRange is enabled, correctness of this nonce is guaranteed.
(height - 1) * near_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER,
implicit_account_id.clone(),
implicit_account_id.clone(),
"test0".parse().unwrap(),
&implicit_account_signer,
*block.hash(),
);
height = check_tx_processing(&mut env, delete_account_tx, height, blocks_number.clone());
let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap().clone();

// Send money to implicit account again, invoking its second creation.
let send_money_again_tx = SignedTransaction::send_money(
2,
"test1".parse().unwrap(),
implicit_account_id.clone(),
&signer1,
deposit_for_account_creation,
*block.hash(),
);
height = check_tx_processing(&mut env, send_money_again_tx, height, blocks_number);
let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap().clone();

// Send money from implicit account with incorrect nonce.
let send_money_from_implicit_account_tx = SignedTransaction::send_money(
1,
implicit_account_id.clone(),
"test0".parse().unwrap(),
&implicit_account_signer,
100,
*block.hash(),
);
let status = env.clients[0].process_tx(send_money_from_implicit_account_tx, false, false);

// Check that sending money from implicit account with correct nonce is still valid.
let send_money_from_implicit_account_tx = SignedTransaction::send_money(
(height - 1) * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER,
implicit_account_id.clone(),
"test0".parse().unwrap(),
&implicit_account_signer,
100,
*block.hash(),
);
check_tx_processing(&mut env, send_money_from_implicit_account_tx, height, blocks_number);

status
}

/// Test that duplicate transactions from implicit accounts are properly rejected.
#[cfg(feature = "protocol_feature_access_key_nonce_for_implicit_accounts")]
#[test]
fn test_transaction_hash_collision_for_implicit_account_fail() {
let protocol_version =
ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version();
assert!(matches!(
Longarithm marked this conversation as resolved.
Show resolved Hide resolved
get_status_of_tx_hash_collision_for_implicit_account(protocol_version),
NetworkClientResponses::InvalidTx(InvalidTxError::InvalidNonce { .. })
));
}

/// Test that duplicate transactions from implicit accounts are not rejected until protocol upgrade.
#[test]
fn test_transaction_hash_collision_for_implicit_account_ok() {
#[cfg(feature = "protocol_feature_access_key_nonce_for_implicit_accounts")]
let protocol_version =
ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version() - 1;
#[cfg(not(feature = "protocol_feature_access_key_nonce_for_implicit_accounts"))]
let protocol_version = PROTOCOL_VERSION;
assert!(matches!(
get_status_of_tx_hash_collision_for_implicit_account(protocol_version),
NetworkClientResponses::ValidTx
));
}

/// Test that chunks with transactions that have expired are considered invalid.
#[test]
fn test_chunk_transaction_validity() {
Expand Down Expand Up @@ -3833,7 +3969,7 @@ mod contract_precompilation_tests {
);

// Wait 3 epochs.
height = produce_epochs(&mut env, 3, EPOCH_LENGTH, height);
height = produce_blocks_from_height(&mut env, 3 * EPOCH_LENGTH, height);

// Process test contract deployment on the first client.
let wasm_code = near_test_contracts::rs_contract().to_vec();
Expand Down Expand Up @@ -3926,7 +4062,7 @@ mod contract_precompilation_tests {
*block.hash(),
);
env.clients[0].process_tx(delete_account_tx, false, false);
height = produce_epochs(&mut env, 1, EPOCH_LENGTH, height);
height = produce_blocks_from_height(&mut env, EPOCH_LENGTH, height);

// Perform state sync for the second client.
state_sync_on_height(&mut env, height - 1);
Expand Down
3 changes: 2 additions & 1 deletion nearcore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ json_rpc = ["near-jsonrpc"]
protocol_feature_alt_bn128 = ["near-primitives/protocol_feature_alt_bn128", "node-runtime/protocol_feature_alt_bn128"]
protocol_feature_chunk_only_producers = ["near-chain-configs/protocol_feature_chunk_only_producers", "near-epoch-manager/protocol_feature_chunk_only_producers", "near-chain/protocol_feature_chunk_only_producers", "near-client/protocol_feature_chunk_only_producers", "node-runtime/protocol_feature_chunk_only_producers", "near-rosetta-rpc/protocol_feature_chunk_only_producers", "near-primitives/protocol_feature_chunk_only_producers"]
protocol_feature_routing_exchange_algorithm = ["near-primitives/protocol_feature_routing_exchange_algorithm", "near-chain/protocol_feature_routing_exchange_algorithm", "near-network/protocol_feature_routing_exchange_algorithm", "near-client/protocol_feature_routing_exchange_algorithm", "near-jsonrpc/protocol_feature_routing_exchange_algorithm"]
nightly_protocol_features = ["nightly_protocol", "near-primitives/nightly_protocol_features", "near-client/nightly_protocol_features", "near-epoch-manager/nightly_protocol_features", "near-store/nightly_protocol_features", "protocol_feature_alt_bn128", "protocol_feature_chunk_only_producers", "protocol_feature_routing_exchange_algorithm"]
protocol_feature_access_key_nonce_for_implicit_accounts = ["near-primitives/protocol_feature_access_key_nonce_for_implicit_accounts", "node-runtime/protocol_feature_access_key_nonce_for_implicit_accounts"]
nightly_protocol_features = ["nightly_protocol", "near-primitives/nightly_protocol_features", "near-client/nightly_protocol_features", "near-epoch-manager/nightly_protocol_features", "near-store/nightly_protocol_features", "protocol_feature_alt_bn128", "protocol_feature_chunk_only_producers", "protocol_feature_routing_exchange_algorithm", "protocol_feature_access_key_nonce_for_implicit_accounts"]
nightly_protocol = ["near-primitives/nightly_protocol", "near-jsonrpc/nightly_protocol"]

# Force usage of a specific wasm vm irrespective of protocol version.
Expand Down
2 changes: 1 addition & 1 deletion runtime/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ near-vm-errors = { path = "../../runtime/near-vm-errors" }
default = []
dump_errors_schema = ["near-vm-errors/dump_errors_schema"]
protocol_feature_chunk_only_producers = ["near-primitives/protocol_feature_chunk_only_producers", "near-store/protocol_feature_chunk_only_producers", "near-chain-configs/protocol_feature_chunk_only_producers"]

protocol_feature_access_key_nonce_for_implicit_accounts = ["near-primitives/protocol_feature_access_key_nonce_for_implicit_accounts"]
no_cpu_compatibility_checks = ["near-vm-runner/no_cpu_compatibility_checks"]

no_cache = ["near-vm-runner/no_cache", "near-store/no_cache"]
Expand Down
17 changes: 15 additions & 2 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use near_primitives::transaction::{
FunctionCallAction, StakeAction, TransferAction,
};
use near_primitives::types::validator_stake::ValidatorStake;
use near_primitives::types::{AccountId, EpochInfoProvider};
use near_primitives::types::{AccountId, BlockHeight, EpochInfoProvider};
use near_primitives::utils::create_random_seed;
use near_primitives::version::{
is_implicit_account_creation_enabled, ProtocolFeature, ProtocolVersion,
Expand Down Expand Up @@ -392,13 +392,26 @@ pub(crate) fn action_implicit_account_creation_transfer(
actor_id: &mut AccountId,
account_id: &AccountId,
transfer: &TransferAction,
block_height: BlockHeight,
current_protocol_version: ProtocolVersion,
) {
// NOTE: The account_id is hex like, because we've checked the permissions before.
debug_assert!(AccountId::is_implicit(account_id.as_ref()));

*actor_id = account_id.clone();

let access_key = AccessKey::full_access();
let mut access_key = AccessKey::full_access();
// Set default nonce for newly created access key to avoid transaction hash collision.
// See <https://github.com/near/nearcore/issues/3779>.
if checked_feature!(
"protocol_feature_access_key_nonce_for_implicit_accounts",
AccessKeyNonceForImplicitAccounts,
current_protocol_version
) {
access_key.nonce = (block_height - 1)
Longarithm marked this conversation as resolved.
Show resolved Hide resolved
* near_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER;
}

// 0 for ED25519
let mut public_key_data = Vec::with_capacity(33);
public_key_data.push(0u8);
Expand Down
2 changes: 2 additions & 0 deletions runtime/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ impl Runtime {
actor_id,
&receipt.receiver_id,
transfer,
apply_state.block_index,
apply_state.current_protocol_version,
);
}
}
Expand Down