diff --git a/tools/fork-network/src/cli.rs b/tools/fork-network/src/cli.rs index cddf572d89d..236db6cdddb 100644 --- a/tools/fork-network/src/cli.rs +++ b/tools/fork-network/src/cli.rs @@ -14,7 +14,6 @@ use near_primitives::account::id::AccountType; use near_primitives::account::{AccessKey, AccessKeyPermission, Account}; use near_primitives::borsh; use near_primitives::hash::CryptoHash; -use near_primitives::receipt::Receipt; use near_primitives::serialize::dec_format; use near_primitives::shard_layout::ShardUId; use near_primitives::state::FlatStateValue; @@ -483,6 +482,8 @@ impl ForkNetworkCommand { tracing::info!(?shard_uid); let mut storage_mutator: SingleShardStorageMutator = make_storage_mutator(prev_state_root)?; + // TODO: allow mutating the state with a secret, so this can be used to prepare a public test network + let default_key = near_mirror::key_mapping::default_extra_key(None).public_key(); // Keeps track of accounts that have a full access key. let mut has_full_key = HashSet::new(); // Lets us lookup large values in the `State` columns. @@ -498,7 +499,6 @@ impl ForkNetworkCommand { let mut contract_data_updated = 0; let mut contract_code_updated = 0; let mut postponed_receipts_updated = 0; - let mut delayed_receipts_updated = 0; let mut received_data_updated = 0; let mut fake_block_height = block_height + 1; for item in store_helper::iter_flat_state_entries(shard_uid, &store, None, None) { @@ -557,23 +557,11 @@ impl ForkNetworkCommand { contract_code_updated += 1; } } - StateRecord::PostponedReceipt(receipt) => { - // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. - if receipt.predecessor_id.get_account_type() - == AccountType::NearImplicitAccount - || receipt.receiver_id.get_account_type() - == AccountType::NearImplicitAccount - { - let new_receipt = Receipt { - predecessor_id: map_account(&receipt.predecessor_id, None), - receiver_id: map_account(&receipt.receiver_id, None), - receipt_id: receipt.receipt_id, - receipt: receipt.receipt.clone(), - }; - storage_mutator.delete_postponed_receipt(receipt)?; - storage_mutator.set_postponed_receipt(&new_receipt)?; - postponed_receipts_updated += 1; - } + StateRecord::PostponedReceipt(mut receipt) => { + storage_mutator.delete_postponed_receipt(&receipt)?; + near_mirror::genesis::map_receipt(&mut receipt, None, &default_key); + storage_mutator.set_postponed_receipt(&receipt)?; + postponed_receipts_updated += 1; } StateRecord::ReceivedData { account_id, data_id, data } => { // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. @@ -584,24 +572,10 @@ impl ForkNetworkCommand { received_data_updated += 1; } } - StateRecord::DelayedReceipt(receipt) => { - // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. - if receipt.predecessor_id.get_account_type() - == AccountType::NearImplicitAccount - || receipt.receiver_id.get_account_type() - == AccountType::NearImplicitAccount - { - let new_receipt = Receipt { - predecessor_id: map_account(&receipt.predecessor_id, None), - receiver_id: map_account(&receipt.receiver_id, None), - receipt_id: receipt.receipt_id, - receipt: receipt.receipt, - }; - storage_mutator.delete_delayed_receipt(index_delayed_receipt)?; - storage_mutator - .set_delayed_receipt(index_delayed_receipt, &new_receipt)?; - delayed_receipts_updated += 1; - } + StateRecord::DelayedReceipt(mut receipt) => { + storage_mutator.delete_delayed_receipt(index_delayed_receipt)?; + near_mirror::genesis::map_receipt(&mut receipt, None, &default_key); + storage_mutator.set_delayed_receipt(index_delayed_receipt, &receipt)?; index_delayed_receipt += 1; } } @@ -619,7 +593,7 @@ impl ForkNetworkCommand { + contract_data_updated + contract_code_updated + postponed_receipts_updated - + delayed_receipts_updated + + index_delayed_receipt + received_data_updated, ); let state_root = storage_mutator.commit(&shard_uid, fake_block_height)?; @@ -638,7 +612,7 @@ impl ForkNetworkCommand { contract_code_updated, contract_data_updated, postponed_receipts_updated, - delayed_receipts_updated, + delayed_receipts_updated = index_delayed_receipt, received_data_updated, num_has_full_key = has_full_key.len(), "Pass 1 done" @@ -669,7 +643,7 @@ impl ForkNetworkCommand { } storage_mutator.set_access_key( account_id, - near_mirror::key_mapping::default_extra_key(None).public_key(), + default_key.clone(), AccessKey::full_access(), )?; num_added += 1; diff --git a/tools/fork-network/src/single_shard_storage_mutator.rs b/tools/fork-network/src/single_shard_storage_mutator.rs index 9c2368b3237..7206dda2e91 100644 --- a/tools/fork-network/src/single_shard_storage_mutator.rs +++ b/tools/fork-network/src/single_shard_storage_mutator.rs @@ -101,9 +101,9 @@ impl SingleShardStorageMutator { ) } - pub(crate) fn delete_postponed_receipt(&mut self, receipt: Box) -> anyhow::Result<()> { + pub(crate) fn delete_postponed_receipt(&mut self, receipt: &Receipt) -> anyhow::Result<()> { self.remove(TrieKey::PostponedReceipt { - receiver_id: receipt.receiver_id, + receiver_id: receipt.receiver_id.clone(), receipt_id: receipt.receipt_id, }) } diff --git a/tools/mirror/src/genesis.rs b/tools/mirror/src/genesis.rs index 3fcfbd67fba..6959dc12faa 100644 --- a/tools/mirror/src/genesis.rs +++ b/tools/mirror/src/genesis.rs @@ -1,4 +1,7 @@ +use near_crypto::PublicKey; +use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; use near_primitives::state_record::StateRecord; +use near_primitives::transaction::{Action, AddKeyAction, DeleteAccountAction, DeleteKeyAction}; use near_primitives_core::account::id::AccountType; use near_primitives_core::account::{AccessKey, AccessKeyPermission}; use serde::ser::{SerializeSeq, Serializer}; @@ -7,10 +10,85 @@ use std::fs::File; use std::io::{BufReader, BufWriter}; use std::path::Path; +// map all the account IDs and keys in this receipt and its actions, and skip any stake actions +fn map_action_receipt( + receipt: &mut ActionReceipt, + secret: Option<&[u8; crate::secret::SECRET_LEN]>, + default_key: &PublicKey, +) { + receipt.signer_id = crate::key_mapping::map_account(&receipt.signer_id, secret); + receipt.signer_public_key = + crate::key_mapping::map_key(&receipt.signer_public_key, secret).public_key(); + for receiver in receipt.output_data_receivers.iter_mut() { + receiver.receiver_id = crate::key_mapping::map_account(&receiver.receiver_id, secret); + } + + let mut actions = Vec::with_capacity(receipt.actions.len()); + let mut account_created = false; + let mut full_key_added = false; + for action in receipt.actions.iter() { + match action { + Action::AddKey(add_key) => { + if add_key.access_key.permission == AccessKeyPermission::FullAccess { + full_key_added = true; + } + let public_key = + crate::key_mapping::map_key(&add_key.public_key, secret).public_key(); + + actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: add_key.access_key.clone(), + }))); + } + Action::DeleteKey(delete_key) => { + let public_key = + crate::key_mapping::map_key(&delete_key.public_key, secret).public_key(); + + actions.push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); + } + Action::DeleteAccount(delete_account) => { + let beneficiary_id = + crate::key_mapping::map_account(&delete_account.beneficiary_id, secret); + actions.push(Action::DeleteAccount(DeleteAccountAction { beneficiary_id })); + } + // We don't want to mess with the set of validators in the target chain + Action::Stake(_) => {} + Action::CreateAccount(_) => { + account_created = true; + actions.push(action.clone()); + } + _ => actions.push(action.clone()), + }; + } + if account_created && !full_key_added { + actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key: default_key.clone(), + access_key: AccessKey::full_access(), + }))); + } + receipt.actions = actions; +} + +// map any account IDs or keys referenced in the receipt +pub fn map_receipt( + receipt: &mut Receipt, + secret: Option<&[u8; crate::secret::SECRET_LEN]>, + default_key: &PublicKey, +) { + receipt.predecessor_id = crate::key_mapping::map_account(&receipt.predecessor_id, secret); + receipt.receiver_id = crate::key_mapping::map_account(&receipt.receiver_id, secret); + match &mut receipt.receipt { + ReceiptEnum::Action(r) => { + map_action_receipt(r, secret, default_key); + } + _ => {} + } +} + /// Reads records, makes changes to them and writes them to a new file. /// `records_file_in` must be different from `records_file_out`. /// Writes a secret to `secret_file_out`. -pub fn map_records>( +pub(crate) fn map_records>( records_file_in: P, records_file_out: P, no_secret: bool, @@ -30,6 +108,7 @@ pub fn map_records>( let mut has_full_key = HashSet::new(); let mut accounts = HashSet::new(); + let default_key = crate::key_mapping::default_extra_key(secret.as_ref()).public_key(); near_chain_configs::stream_records_from_file(reader, |mut r| { match &mut r { StateRecord::AccessKey { account_id, public_key, access_key } => { @@ -73,15 +152,7 @@ pub fn map_records>( records_seq.serialize_element(&r).unwrap(); } StateRecord::PostponedReceipt(receipt) => { - // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. - if receipt.predecessor_id.get_account_type() == AccountType::NearImplicitAccount - || receipt.receiver_id.get_account_type() == AccountType::NearImplicitAccount - { - receipt.predecessor_id = - crate::key_mapping::map_account(&receipt.predecessor_id, secret.as_ref()); - receipt.receiver_id = - crate::key_mapping::map_account(&receipt.receiver_id, secret.as_ref()); - } + map_receipt(receipt, secret.as_ref(), &default_key); records_seq.serialize_element(&r).unwrap(); } StateRecord::ReceivedData { account_id, .. } => { @@ -92,25 +163,17 @@ pub fn map_records>( records_seq.serialize_element(&r).unwrap(); } StateRecord::DelayedReceipt(receipt) => { - // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. - if receipt.predecessor_id.get_account_type() == AccountType::NearImplicitAccount - || receipt.receiver_id.get_account_type() == AccountType::NearImplicitAccount - { - receipt.predecessor_id = - crate::key_mapping::map_account(&receipt.predecessor_id, secret.as_ref()); - receipt.receiver_id = - crate::key_mapping::map_account(&receipt.receiver_id, secret.as_ref()); - } + map_receipt(receipt, secret.as_ref(), &default_key); records_seq.serialize_element(&r).unwrap(); } }; })?; - let default_key = crate::key_mapping::default_extra_key(secret.as_ref()); + for account_id in accounts { if !has_full_key.contains(&account_id) { records_seq.serialize_element(&StateRecord::AccessKey { account_id, - public_key: default_key.public_key(), + public_key: default_key.clone(), access_key: AccessKey::full_access(), })?; } diff --git a/tools/mirror/src/lib.rs b/tools/mirror/src/lib.rs index 80e5870ac73..dea4d38882e 100644 --- a/tools/mirror/src/lib.rs +++ b/tools/mirror/src/lib.rs @@ -16,8 +16,8 @@ use near_o11y::WithSpanContextExt; use near_primitives::hash::CryptoHash; use near_primitives::receipt::{Receipt, ReceiptEnum}; use near_primitives::transaction::{ - Action, AddKeyAction, CreateAccountAction, DeleteKeyAction, SignedTransaction, StakeAction, - Transaction, + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + SignedTransaction, StakeAction, Transaction, }; use near_primitives::types::{ AccountId, BlockHeight, BlockReference, Finality, TransactionOrReceiptId, @@ -41,7 +41,7 @@ use tokio::sync::mpsc; mod chain_tracker; pub mod cli; -mod genesis; +pub mod genesis; pub mod key_mapping; mod metrics; mod offline; @@ -1032,6 +1032,15 @@ impl TxMirror { account_created = true; actions.push(action.clone()); } + Action::DeleteAccount(d) => { + actions.push(Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: crate::key_mapping::map_account( + &d.beneficiary_id, + self.secret.as_ref(), + ), + })); + } + // TODO: handle delegate actions _ => actions.push(action.clone()), }; }