From a9a6eff1121dc48c4aa6a0e4891ef29542e41b84 Mon Sep 17 00:00:00 2001 From: Bowen Wang Date: Tue, 14 May 2024 07:57:19 -0700 Subject: [PATCH] Introduce priority fee field for transaction and receipt (#11228) A first step towards https://github.com/near/NEPs/pull/541 by introducing a priority field in both transaction and receipt. This is not entirely trivial due to the need to maintain backward compatibility. This PR accomplishes backward compatibility by leveraging the account id serialization and implement manual deserialization for the new transaction and receipt structures. While this PR appears to be quite large, most of the changes are trivial. The core of the changes are the serialization/deserialization of transaction and receipt. While this change introduces the new versions, they are prohibited from being used in the current protocol until the introduction of the protocol change that leverages priorities. --- chain/chain/src/chain.rs | 10 +- chain/chain/src/chain_update.rs | 5 +- chain/chain/src/garbage_collection.rs | 7 +- chain/chain/src/runtime/mod.rs | 4 +- chain/chain/src/runtime/tests.rs | 7 +- chain/chain/src/store/mod.rs | 10 +- chain/chain/src/test_utils.rs | 6 +- chain/chain/src/test_utils/kv_runtime.rs | 48 +-- chain/chain/src/validate.rs | 4 +- chain/chunks/src/client.rs | 6 +- chain/client/src/client.rs | 9 +- chain/client/src/test_utils/test_env.rs | 2 + chain/indexer/src/streamer/utils.rs | 42 +-- .../src/types/transactions.rs | 2 +- chain/jsonrpc/res/rpc_errors_schema.json | 8 +- chain/jsonrpc/src/lib.rs | 2 +- chain/pool/src/lib.rs | 24 +- chain/rosetta-rpc/src/lib.rs | 44 +-- core/primitives/benches/serialization.rs | 8 +- core/primitives/src/errors.rs | 5 + core/primitives/src/receipt.rs | 332 ++++++++++++++++-- core/primitives/src/state_record.rs | 2 +- core/primitives/src/test_utils.rs | 80 ++++- core/primitives/src/transaction.rs | 239 ++++++++++++- core/primitives/src/views.rs | 41 ++- core/store/benches/finalize_bench.rs | 10 +- core/store/src/genesis/state_applier.rs | 10 +- core/store/src/lib.rs | 10 +- core/store/src/test_utils.rs | 18 +- core/store/src/trie/resharding.rs | 10 +- .../genesis-csv-to-json/src/csv_parser.rs | 5 +- .../src/tests/client/benchmarks.rs | 1 + .../src/tests/client/cold_storage.rs | 4 +- .../src/tests/client/epoch_sync.rs | 2 + .../account_id_in_function_call_permission.rs | 19 +- .../client/features/chunk_nodes_cache.rs | 1 + .../src/tests/client/features/flat_storage.rs | 14 +- .../features/increase_storage_compute_cost.rs | 1 + .../features/lower_storage_key_limit.rs | 33 +- .../src/tests/client/features/nearvm.rs | 15 +- .../client/features/nonrefundable_transfer.rs | 1 + ...restore_receipts_after_fix_apply_chunks.rs | 2 +- .../features/storage_proof_size_limit.rs | 4 +- .../tests/client/features/wallet_contract.rs | 4 + .../tests/client/features/yield_timeouts.rs | 8 +- .../client/features/zero_balance_account.rs | 2 + .../src/tests/client/process_blocks.rs | 17 +- .../src/tests/client/resharding.rs | 14 +- integration-tests/src/tests/client/sandbox.rs | 3 +- .../src/tests/nearcore/rpc_nodes.rs | 2 +- .../src/tests/runtime/deployment.rs | 1 + .../src/tests/standard_cases/mod.rs | 8 +- integration-tests/src/tests/test_errors.rs | 2 + integration-tests/src/user/mod.rs | 1 + integration-tests/src/user/runtime_user.rs | 2 +- nearcore/src/metrics.rs | 4 +- .../src/action_costs.rs | 6 +- .../src/transaction_builder.rs | 1 + runtime/runtime/src/actions.rs | 49 ++- runtime/runtime/src/balance_checker.rs | 76 ++-- runtime/runtime/src/config.rs | 15 +- runtime/runtime/src/congestion_control.rs | 6 +- runtime/runtime/src/lib.rs | 170 +++++---- runtime/runtime/src/prefetch.rs | 20 +- runtime/runtime/src/verifier.rs | 86 +++-- .../runtime/tests/runtime_group_tools/mod.rs | 11 +- runtime/runtime/tests/test_async_calls.rs | 13 + test-utils/runtime-tester/src/run_test.rs | 1 + .../src/single_shard_storage_mutator.rs | 8 +- tools/mirror/src/chain_tracker.rs | 64 ++-- tools/mirror/src/genesis.rs | 24 +- tools/mirror/src/lib.rs | 58 +-- tools/state-viewer/src/apply_chain_range.rs | 2 +- tools/state-viewer/src/apply_chunk.rs | 6 +- tools/state-viewer/src/contract_accounts.rs | 14 +- tools/state-viewer/src/state_dump.rs | 1 + tools/state-viewer/src/tx_dump.rs | 2 +- 77 files changed, 1279 insertions(+), 529 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 8d73a3eee56..06d48a500f9 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -3011,7 +3011,7 @@ impl Chain { self.chain_store() .check_transaction_validity_period( prev_block_header, - &transaction.transaction.block_hash, + transaction.transaction.block_hash(), transaction_validity_period, ) .map_err(|_| Error::from(Error::InvalidTransactions))?; @@ -3029,7 +3029,7 @@ impl Chain { self.chain_store() .check_transaction_validity_period( &prev_block_header, - &tx.transaction.block_hash, + tx.transaction.block_hash(), self.transaction_validity_period, ) .is_ok() @@ -4406,7 +4406,7 @@ impl Chain { ) -> HashMap> { let mut result = HashMap::new(); for receipt in receipts { - let shard_id = account_id_to_shard_id(&receipt.receiver_id, shard_layout); + let shard_id = account_id_to_shard_id(receipt.receiver_id(), shard_layout); let entry = result.entry(shard_id).or_insert_with(Vec::new); entry.push(receipt) } @@ -4427,8 +4427,8 @@ impl Chain { let mut cache = HashMap::new(); for receipt in receipts { let &mut shard_id = cache - .entry(&receipt.receiver_id) - .or_insert_with(|| account_id_to_shard_id(&receipt.receiver_id, shard_layout)); + .entry(receipt.receiver_id()) + .or_insert_with(|| account_id_to_shard_id(receipt.receiver_id(), shard_layout)); // This unwrap should be safe as we pre-populated the map with all // valid shard ids. result.get_mut(&shard_id).unwrap().push(receipt); diff --git a/chain/chain/src/chain_update.rs b/chain/chain/src/chain_update.rs index 6a792945011..37b5c80c910 100644 --- a/chain/chain/src/chain_update.rs +++ b/chain/chain/src/chain_update.rs @@ -137,7 +137,10 @@ impl<'a> ChainUpdate<'a> { let outgoing_receipts = outgoing_receipts .iter() .map(|receipt| { - (receipt.receipt_id, account_id_to_shard_id(&receipt.receiver_id, &shard_layout)) + ( + *receipt.receipt_id(), + account_id_to_shard_id(receipt.receiver_id(), &shard_layout), + ) }) .collect(); Ok(outgoing_receipts) diff --git a/chain/chain/src/garbage_collection.rs b/chain/chain/src/garbage_collection.rs index 13ba45c91ca..d719dc2a9c3 100644 --- a/chain/chain/src/garbage_collection.rs +++ b/chain/chain/src/garbage_collection.rs @@ -876,10 +876,9 @@ impl<'a> ChainStoreUpdate<'a> { fn gc_outgoing_receipts(&mut self, block_hash: &CryptoHash, shard_id: ShardId) { let mut store_update = self.store().store_update(); - match self - .get_outgoing_receipts(block_hash, shard_id) - .map(|receipts| receipts.iter().map(|receipt| receipt.receipt_id).collect::>()) - { + match self.get_outgoing_receipts(block_hash, shard_id).map(|receipts| { + receipts.iter().map(|receipt| *receipt.receipt_id()).collect::>() + }) { Ok(receipt_ids) => { for receipt_id in receipt_ids { let key: Vec = receipt_id.into(); diff --git a/chain/chain/src/runtime/mod.rs b/chain/chain/src/runtime/mod.rs index 22a44f8c2e7..8175b56d1b2 100644 --- a/chain/chain/src/runtime/mod.rs +++ b/chain/chain/src/runtime/mod.rs @@ -650,7 +650,7 @@ impl RuntimeAdapter for NightshadeRuntime { if let Some(state_root) = state_root { let shard_uid = - self.account_id_to_shard_uid(&transaction.transaction.signer_id, epoch_id)?; + self.account_id_to_shard_uid(transaction.transaction.signer_id(), epoch_id)?; let mut state_update = self.tries.new_trie_update(shard_uid, state_root); match verify_and_charge_transaction( @@ -825,7 +825,7 @@ impl RuntimeAdapter for NightshadeRuntime { if ProtocolFeature::CongestionControl.enabled(protocol_version) { let receiving_shard = EpochManagerAdapter::account_id_to_shard_id( self.epoch_manager.as_ref(), - &tx.transaction.receiver_id, + tx.transaction.receiver_id(), &epoch_id, )?; if let Some(congestion_info) = diff --git a/chain/chain/src/runtime/tests.rs b/chain/chain/src/runtime/tests.rs index 073ab173b46..fcce5a86802 100644 --- a/chain/chain/src/runtime/tests.rs +++ b/chain/chain/src/runtime/tests.rs @@ -60,6 +60,7 @@ fn stake( vec![Action::Stake(Box::new(StakeAction { stake, public_key: sender.public_key() }))], // runtime does not validate block history CryptoHash::default(), + 0, ) } @@ -356,7 +357,7 @@ impl TestEnv { let shard_layout = self.epoch_manager.get_shard_layout_from_prev_block(&new_hash).unwrap(); let mut new_receipts = HashMap::<_, Vec>::new(); for receipt in all_receipts { - let shard_id = account_id_to_shard_id(&receipt.receiver_id, &shard_layout); + let shard_id = account_id_to_shard_id(receipt.receiver_id(), &shard_layout); new_receipts.entry(shard_id).or_default().push(receipt); } self.last_receipts = new_receipts; @@ -1390,6 +1391,7 @@ fn test_delete_account_after_unstake() { })], // runtime does not validate block history CryptoHash::default(), + 0, ); env.step_default(vec![delete_account_transaction]); for _ in 15..=17 { @@ -1481,6 +1483,7 @@ fn test_trie_and_flat_state_equality() { vec![Action::Transfer(TransferAction { deposit: 10 })], // runtime does not validate block history CryptoHash::default(), + 0, ); env.step_default(vec![transfer_tx]); for _ in 1..=5 { @@ -1651,7 +1654,7 @@ fn prepare_transactions( .chain_store() .check_transaction_validity_period( &chain.get_block_header(&env.head.prev_block_hash).unwrap(), - &tx.transaction.block_hash, + tx.transaction.block_hash(), chain.transaction_validity_period, ) .is_ok() diff --git a/chain/chain/src/store/mod.rs b/chain/chain/src/store/mod.rs index 865a046c251..15cd3614290 100644 --- a/chain/chain/src/store/mod.rs +++ b/chain/chain/src/store/mod.rs @@ -383,12 +383,12 @@ fn filter_incoming_receipts_for_shard( let ReceiptProof(receipts, shard_proof) = receipt_proof.clone(); for receipt in receipts { let receiver_shard_id = - account_id_to_shard_id(&receipt.receiver_id, target_shard_layout); + account_id_to_shard_id(receipt.receiver_id(), target_shard_layout); if receiver_shard_id == target_shard_id { - tracing::trace!(target: "chain", receipt_id=?receipt.receipt_id, "including receipt"); + tracing::trace!(target: "chain", receipt_id=?receipt.receipt_id(), "including receipt"); filtered_receipts.push(receipt); } else { - tracing::trace!(target: "chain", receipt_id=?receipt.receipt_id, "excluding receipt"); + tracing::trace!(target: "chain", receipt_id=?receipt.receipt_id(), "excluding receipt"); } } // TODO(resharding) adjust the shard proof accordingly @@ -689,7 +689,7 @@ impl ChainStore { shard_id: ShardId, ) -> Result<(), Error> { receipts.retain(|receipt| { - account_id_to_shard_id(&receipt.receiver_id, &shard_layout) == shard_id + account_id_to_shard_id(receipt.receiver_id(), &shard_layout) == shard_id }); Ok(()) } @@ -1977,7 +1977,7 @@ impl<'a> ChainStoreUpdate<'a> { for receipt in chunk.prev_outgoing_receipts() { self.chain_store_cache_update .receipts - .insert(receipt.receipt_id, Arc::new(receipt.clone())); + .insert(*receipt.receipt_id(), Arc::new(receipt.clone())); } self.chain_store_cache_update.chunks.insert(chunk.chunk_hash(), Arc::new(chunk)); } diff --git a/chain/chain/src/test_utils.rs b/chain/chain/src/test_utils.rs index 849c62f5d28..0fe9ffc1387 100644 --- a/chain/chain/src/test_utils.rs +++ b/chain/chain/src/test_utils.rs @@ -276,7 +276,7 @@ mod test { use rand::Rng; use near_primitives::hash::CryptoHash; - use near_primitives::receipt::Receipt; + use near_primitives::receipt::{Receipt, ReceiptPriority}; use near_primitives::sharding::ReceiptList; use near_primitives::types::{AccountId, NumShards}; @@ -293,7 +293,7 @@ mod test { let shard_receipts: Vec = receipts .iter() .filter(|&receipt| { - account_id_to_shard_id(&receipt.receiver_id, shard_layout) == shard_id + account_id_to_shard_id(receipt.receiver_id(), shard_layout) == shard_id }) .cloned() .collect(); @@ -305,7 +305,7 @@ mod test { fn test_build_receipt_hashes_with_num_shard(num_shards: NumShards) { let shard_layout = ShardLayout::v0(num_shards, 0); let create_receipt_from_receiver_id = - |receiver_id| Receipt::new_balance_refund(&receiver_id, 0); + |receiver_id| Receipt::new_balance_refund(&receiver_id, 0, ReceiptPriority::NoPriority); let mut rng = rand::thread_rng(); let receipts = (0..3000) .map(|_| { diff --git a/chain/chain/src/test_utils/kv_runtime.rs b/chain/chain/src/test_utils/kv_runtime.rs index a3ab495a032..fb5f6388ad1 100644 --- a/chain/chain/src/test_utils/kv_runtime.rs +++ b/chain/chain/src/test_utils/kv_runtime.rs @@ -25,7 +25,7 @@ use near_primitives::epoch_manager::ShardConfig; use near_primitives::epoch_manager::ValidatorSelectionConfig; use near_primitives::errors::{EpochError, InvalidTxError}; use near_primitives::hash::{hash, CryptoHash}; -use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; +use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum, ReceiptV0}; use near_primitives::shard_layout::{ShardLayout, ShardUId}; use near_primitives::sharding::{ChunkHash, ShardChunkHeader}; use near_primitives::state_part::PartId; @@ -1145,16 +1145,19 @@ impl RuntimeAdapter for KeyValueRuntime { for receipt in receipts.iter() { if let ReceiptEnum::Action(action) | ReceiptEnum::PromiseYield(action) = - &receipt.receipt + receipt.receipt() { - assert_eq!(account_id_to_shard_id(&receipt.receiver_id, self.num_shards), shard_id); - if !state.receipt_nonces.contains(&receipt.receipt_id) { - state.receipt_nonces.insert(receipt.receipt_id); + assert_eq!( + account_id_to_shard_id(receipt.receiver_id(), self.num_shards), + shard_id + ); + if !state.receipt_nonces.contains(receipt.receipt_id()) { + state.receipt_nonces.insert(*receipt.receipt_id()); if let Action::Transfer(TransferAction { deposit }) = action.actions[0] { balance_transfers.push(( receipt.get_hash(), - receipt.predecessor_id.clone(), - receipt.receiver_id.clone(), + receipt.predecessor_id().clone(), + receipt.receiver_id().clone(), deposit, 0, )); @@ -1169,36 +1172,37 @@ impl RuntimeAdapter for KeyValueRuntime { for transaction in transactions { assert_eq!( - account_id_to_shard_id(&transaction.transaction.signer_id, self.num_shards), + account_id_to_shard_id(transaction.transaction.signer_id(), self.num_shards), shard_id ); - if transaction.transaction.actions.is_empty() { + if transaction.transaction.actions().is_empty() { continue; } - if let Action::Transfer(TransferAction { deposit }) = transaction.transaction.actions[0] + if let Action::Transfer(TransferAction { deposit }) = + transaction.transaction.actions()[0] { if !state.tx_nonces.contains(&AccountNonce( - transaction.transaction.receiver_id.clone(), - transaction.transaction.nonce, + transaction.transaction.receiver_id().clone(), + transaction.transaction.nonce(), )) { state.tx_nonces.insert(AccountNonce( - transaction.transaction.receiver_id.clone(), - transaction.transaction.nonce, + transaction.transaction.receiver_id().clone(), + transaction.transaction.nonce(), )); balance_transfers.push(( transaction.get_hash(), - transaction.transaction.signer_id.clone(), - transaction.transaction.receiver_id.clone(), + transaction.transaction.signer_id().clone(), + transaction.transaction.receiver_id().clone(), deposit, - transaction.transaction.nonce, + transaction.transaction.nonce(), )); } else { balance_transfers.push(( transaction.get_hash(), - transaction.transaction.signer_id.clone(), - transaction.transaction.receiver_id.clone(), + transaction.transaction.signer_id().clone(), + transaction.transaction.receiver_id().clone(), 0, - transaction.transaction.nonce, + transaction.transaction.nonce(), )); } } else { @@ -1229,7 +1233,7 @@ impl RuntimeAdapter for KeyValueRuntime { vec![] } else { assert_ne!(nonce, 0); - let receipt = Receipt { + let receipt = Receipt::V0(ReceiptV0 { predecessor_id: from.clone(), receiver_id: to.clone(), receipt_id: create_receipt_nonce(from.clone(), to.clone(), amount, nonce), @@ -1241,7 +1245,7 @@ impl RuntimeAdapter for KeyValueRuntime { input_data_ids: vec![], actions: vec![Action::Transfer(TransferAction { deposit: amount })], }), - }; + }); let receipt_hash = receipt.get_hash(); outgoing_receipts.push(receipt); vec![receipt_hash] diff --git a/chain/chain/src/validate.rs b/chain/chain/src/validate.rs index a6c5d4d4849..02e9aa8cea8 100644 --- a/chain/chain/src/validate.rs +++ b/chain/chain/src/validate.rs @@ -78,10 +78,10 @@ pub fn validate_transactions_order(transactions: &[SignedTransaction]) -> bool { let mut current_batch = 1; for tx in transactions { - let key = (&tx.transaction.signer_id, &tx.transaction.public_key); + let key = (tx.transaction.signer_id(), tx.transaction.public_key()); // Verifying nonce - let nonce = tx.transaction.nonce; + let nonce = tx.transaction.nonce(); if let Some(last_nonce) = nonces.get(&key) { if nonce <= *last_nonce { // Nonces should increase. diff --git a/chain/chunks/src/client.rs b/chain/chunks/src/client.rs index b360db58bfc..5678f0ef5fe 100644 --- a/chain/chunks/src/client.rs +++ b/chain/chunks/src/client.rs @@ -143,7 +143,7 @@ impl ShardedTransactionPool { } for tx in transactions { - let signer_id = &tx.transaction.signer_id; + let signer_id = tx.transaction.signer_id(); let new_shard_uid = account_id_to_shard_uid(&signer_id, new_shard_layout); self.insert_transaction(new_shard_uid, tx); } @@ -266,8 +266,8 @@ mod tests { while let Some(group) = pool_iter.next() { while let Some(tx) = group.next() { total += 1; - let account_id = tx.transaction.signer_id; - let tx_shard_uid = account_id_to_shard_uid(&account_id, &new_shard_layout); + let account_id = tx.transaction.signer_id(); + let tx_shard_uid = account_id_to_shard_uid(account_id, &new_shard_layout); tracing::debug!("checking {account_id:?}:{tx_shard_uid} in {shard_uid}"); assert_eq!(shard_uid, tx_shard_uid); } diff --git a/chain/client/src/client.rs b/chain/client/src/client.rs index a4777f5dd87..e0f1651caa2 100644 --- a/chain/client/src/client.rs +++ b/chain/client/src/client.rs @@ -1013,6 +1013,7 @@ impl Client { "other".parse().unwrap(), 3, prev_block_hash, + 0, ), )); if txs.storage_proof.is_none() { @@ -2118,7 +2119,7 @@ impl Client { /// Forwards given transaction to upcoming validators. fn forward_tx(&self, epoch_id: &EpochId, tx: &SignedTransaction) -> Result<(), Error> { let shard_id = - self.epoch_manager.account_id_to_shard_id(&tx.transaction.signer_id, epoch_id)?; + self.epoch_manager.account_id_to_shard_id(tx.transaction.signer_id(), epoch_id)?; // Use the header head to make sure the list of validators is as // up-to-date as possible. let head = self.chain.header_head()?; @@ -2135,7 +2136,7 @@ impl Client { if let Some(next_epoch_id) = &maybe_next_epoch_id { let next_shard_id = self .epoch_manager - .account_id_to_shard_id(&tx.transaction.signer_id, next_epoch_id)?; + .account_id_to_shard_id(tx.transaction.signer_id(), next_epoch_id)?; let validator = self.epoch_manager.get_chunk_producer( next_epoch_id, target_height, @@ -2226,7 +2227,7 @@ impl Client { // `cur_block_header`. if let Err(e) = self.chain.chain_store().check_transaction_validity_period( &cur_block_header, - &tx.transaction.block_hash, + tx.transaction.block_hash(), transaction_validity_period, ) { debug!(target: "client", ?tx, "Invalid tx: expired or from a different fork"); @@ -2247,7 +2248,7 @@ impl Client { } let shard_id = - self.epoch_manager.account_id_to_shard_id(&tx.transaction.signer_id, &epoch_id)?; + self.epoch_manager.account_id_to_shard_id(tx.transaction.signer_id(), &epoch_id)?; let care_about_shard = self.shard_tracker.care_about_shard(me, &head.last_block_hash, shard_id, true); let will_care_about_shard = diff --git a/chain/client/src/test_utils/test_env.rs b/chain/client/src/test_utils/test_env.rs index 54285692b66..dcb858eb902 100644 --- a/chain/client/src/test_utils/test_env.rs +++ b/chain/client/src/test_utils/test_env.rs @@ -697,6 +697,7 @@ impl TestEnv { signer, actions, tip.last_block_hash, + 0, ) } @@ -735,6 +736,7 @@ impl TestEnv { &relayer_signer, vec![Action::Delegate(Box::new(signed_delegate_action))], tip.last_block_hash, + 0, ) } diff --git a/chain/indexer/src/streamer/utils.rs b/chain/indexer/src/streamer/utils.rs index 15d559b8ac8..6c8a14c7440 100644 --- a/chain/indexer/src/streamer/utils.rs +++ b/chain/indexer/src/streamer/utils.rs @@ -27,26 +27,29 @@ pub(crate) async fn convert_transactions_sir_into_local_receipts( .map(|tx| { let cost = tx_cost( &runtime_config, - &near_primitives::transaction::Transaction { - signer_id: tx.transaction.signer_id.clone(), - public_key: tx.transaction.public_key.clone(), - nonce: tx.transaction.nonce, - receiver_id: tx.transaction.receiver_id.clone(), - block_hash: block.header.hash, - actions: tx - .transaction - .actions - .clone() - .into_iter() - .map(|action| { - near_primitives::transaction::Action::try_from(action).unwrap() - }) - .collect(), - }, + &near_primitives::transaction::Transaction::V0( + near_primitives::transaction::TransactionV0 { + signer_id: tx.transaction.signer_id.clone(), + public_key: tx.transaction.public_key.clone(), + nonce: tx.transaction.nonce, + receiver_id: tx.transaction.receiver_id.clone(), + block_hash: block.header.hash, + actions: tx + .transaction + .actions + .clone() + .into_iter() + .map(|action| { + near_primitives::transaction::Action::try_from(action).unwrap() + }) + .collect(), + }, + ), prev_block_gas_price, true, protocol_version, - ); + ) + .expect("TransactionCost returned IntegerOverflowError"); views::ReceiptView { predecessor_id: tx.transaction.signer_id.clone(), receiver_id: tx.transaction.receiver_id.clone(), @@ -56,14 +59,13 @@ pub(crate) async fn convert_transactions_sir_into_local_receipts( receipt: views::ReceiptEnumView::Action { signer_id: tx.transaction.signer_id.clone(), signer_public_key: tx.transaction.public_key.clone(), - gas_price: cost - .expect("TransactionCost returned IntegerOverflowError") - .receipt_gas_price, + gas_price: cost.receipt_gas_price, output_data_receivers: vec![], input_data_ids: vec![], actions: tx.transaction.actions.clone(), is_promise_yield: false, }, + priority: 0, } }) .collect(); diff --git a/chain/jsonrpc-primitives/src/types/transactions.rs b/chain/jsonrpc-primitives/src/types/transactions.rs index f6f0efc49c3..30d00838035 100644 --- a/chain/jsonrpc-primitives/src/types/transactions.rs +++ b/chain/jsonrpc-primitives/src/types/transactions.rs @@ -81,7 +81,7 @@ impl TransactionInfo { match self { TransactionInfo::Transaction(tx) => match tx { SignedTransaction::SignedTransaction(tx) => { - (tx.get_hash(), &tx.transaction.signer_id) + (tx.get_hash(), tx.transaction.signer_id()) } }, TransactionInfo::TransactionId { tx_hash, sender_account_id } => { diff --git a/chain/jsonrpc/res/rpc_errors_schema.json b/chain/jsonrpc/res/rpc_errors_schema.json index 5725f0c95e1..a09dfebc2fe 100644 --- a/chain/jsonrpc/res/rpc_errors_schema.json +++ b/chain/jsonrpc/res/rpc_errors_schema.json @@ -547,6 +547,11 @@ "account_id": "" } }, + "InvalidTransactionVersion": { + "name": "InvalidTransactionVersion", + "subtypes": [], + "props": {} + }, "InvalidTxError": { "name": "InvalidTxError", "subtypes": [ @@ -563,7 +568,8 @@ "InvalidChain", "Expired", "ActionsValidation", - "TransactionSizeExceeded" + "TransactionSizeExceeded", + "InvalidTransactionVersion" ], "props": {} }, diff --git a/chain/jsonrpc/src/lib.rs b/chain/jsonrpc/src/lib.rs index e12f81c4e7f..5504e22ab53 100644 --- a/chain/jsonrpc/src/lib.rs +++ b/chain/jsonrpc/src/lib.rs @@ -659,7 +659,7 @@ impl JsonRpcHandler { ) -> Result { let tx_hash = tx.get_hash(); - let signer_account_id = tx.transaction.signer_id.clone(); + let signer_account_id = tx.transaction.signer_id().clone(); let response = self .client_sender .send_async(ProcessTxRequest { transaction: tx, is_forwarded: false, check_only }) diff --git a/chain/pool/src/lib.rs b/chain/pool/src/lib.rs index 56708b95507..9a28a374502 100644 --- a/chain/pool/src/lib.rs +++ b/chain/pool/src/lib.rs @@ -103,8 +103,8 @@ impl TransactionPool { // At this point transaction is accepted to the pool. self.total_transaction_size = new_total_transaction_size; - let signer_id = &signed_transaction.transaction.signer_id; - let signer_public_key = &signed_transaction.transaction.public_key; + let signer_id = signed_transaction.transaction.signer_id(); + let signer_public_key = signed_transaction.transaction.public_key(); self.transactions .entry(self.key(signer_id, signer_public_key)) .or_insert_with(Vec::new) @@ -134,8 +134,8 @@ impl TransactionPool { continue; } - let signer_id = &tx.transaction.signer_id; - let signer_public_key = &tx.transaction.public_key; + let signer_id = tx.transaction.signer_id(); + let signer_public_key = tx.transaction.public_key(); grouped_transactions .entry(self.key(signer_id, signer_public_key)) .or_insert_with(HashSet::new) @@ -230,7 +230,7 @@ impl<'a> TransactionGroupIterator for PoolIteratorWrapper<'a> { self.pool.last_used_key = key; let mut transactions = self.pool.transactions.remove(&key).expect("just checked existence"); - transactions.sort_by_key(|st| std::cmp::Reverse(st.transaction.nonce)); + transactions.sort_by_key(|st| std::cmp::Reverse(st.transaction.nonce())); self.sorted_groups.push_back(TransactionGroup { key, transactions, @@ -380,7 +380,7 @@ mod tests { ( prepare_transactions(&mut pool, expected_weight) .iter() - .map(|tx| tx.transaction.nonce) + .map(|tx| tx.transaction.nonce()) .collect(), pool, ) @@ -455,7 +455,7 @@ mod tests { sort_pairs(&mut nonces[..6]); assert_eq!(nonces, vec![1, 21, 2, 22, 3, 23, 24, 25, 26, 27]); let nonces: Vec = - prepare_transactions(&mut pool, 10).iter().map(|tx| tx.transaction.nonce).collect(); + prepare_transactions(&mut pool, 10).iter().map(|tx| tx.transaction.nonce()).collect(); assert_eq!(nonces, vec![28, 29, 30, 31]); } @@ -498,9 +498,9 @@ mod tests { assert_eq!(pool.len(), txs_to_check.len()); let mut pool_txs = prepare_transactions(&mut pool, txs_to_check.len() as u32); - pool_txs.sort_by_key(|tx| tx.transaction.nonce); + pool_txs.sort_by_key(|tx| tx.transaction.nonce()); let mut expected_txs = txs_to_check.to_vec(); - expected_txs.sort_by_key(|tx| tx.transaction.nonce); + expected_txs.sort_by_key(|tx| tx.transaction.nonce()); assert_eq!(pool_txs, expected_txs); } @@ -518,7 +518,7 @@ mod tests { let mut pool_iter = pool.pool_iterator(); while let Some(iter) = pool_iter.next() { while let Some(tx) = iter.next() { - if tx.transaction.nonce & 1 == 1 { + if tx.transaction.nonce() & 1 == 1 { res.push(tx); break; } @@ -527,7 +527,7 @@ mod tests { drop(pool_iter); assert_eq!(pool.len(), 0); assert_eq!(pool.transaction_size(), 0); - let mut nonces: Vec<_> = res.into_iter().map(|tx| tx.transaction.nonce).collect(); + let mut nonces: Vec<_> = res.into_iter().map(|tx| tx.transaction.nonce()).collect(); sort_pairs(&mut nonces[..4]); assert_eq!(nonces, vec![1, 21, 3, 23, 25, 27, 29, 31]); } @@ -590,7 +590,7 @@ mod tests { let txs = prepare_transactions(&mut pool, 5); assert_eq!(txs.len(), 5); nonces.sort(); - let mut new_nonces = txs.iter().map(|tx| tx.transaction.nonce).collect::>(); + let mut new_nonces = txs.iter().map(|tx| tx.transaction.nonce()).collect::>(); new_nonces.sort(); assert_ne!(nonces, new_nonces); } diff --git a/chain/rosetta-rpc/src/lib.rs b/chain/rosetta-rpc/src/lib.rs index e8be28019ed..99965c02e77 100644 --- a/chain/rosetta-rpc/src/lib.rs +++ b/chain/rosetta-rpc/src/lib.rs @@ -650,19 +650,21 @@ async fn construction_payloads( } = operations.try_into()?; let models::ConstructionMetadata { recent_block_hash, signer_public_access_key_nonce } = metadata; - let unsigned_transaction = near_primitives::transaction::Transaction { - block_hash: recent_block_hash.parse().map_err(|err| { - errors::ErrorKind::InvalidInput(format!( - "block hash could not be parsed due to: {:?}", - err - )) - })?, - signer_id: signer_account_id.clone(), - public_key: signer_public_access_key.clone(), - nonce: signer_public_access_key_nonce, - receiver_id: receiver_account_id, - actions, - }; + let unsigned_transaction = near_primitives::transaction::Transaction::V0( + near_primitives::transaction::TransactionV0 { + block_hash: recent_block_hash.parse().map_err(|err| { + errors::ErrorKind::InvalidInput(format!( + "block hash could not be parsed due to: {:?}", + err + )) + })?, + signer_id: signer_account_id.clone(), + public_key: signer_public_access_key.clone(), + nonce: signer_public_access_key_nonce, + receiver_id: receiver_account_id, + actions, + }, + ); let (transaction_hash, _) = unsigned_transaction.get_hash_and_size(); @@ -727,12 +729,7 @@ async fn construction_parse( check_network_identifier(&client_addr, network_identifier).await?; - let near_primitives::transaction::Transaction { - actions, - signer_id: sender_account_id, - receiver_id: receiver_account_id, - .. - } = if signed { + let transaction = if signed { near_primitives::transaction::SignedTransaction::try_from_slice(&transaction.into_inner()) .map_err(|err| { errors::ErrorKind::InvalidInput(format!( @@ -752,10 +749,13 @@ async fn construction_parse( }; let account_identifier_signers = - if signed { vec![sender_account_id.clone().into()] } else { vec![] }; + if signed { vec![transaction.signer_id().clone().into()] } else { vec![] }; - let near_actions = - crate::adapters::NearActions { sender_account_id, receiver_account_id, actions }; + let near_actions = crate::adapters::NearActions { + sender_account_id: transaction.signer_id().clone(), + receiver_account_id: transaction.receiver_id().clone(), + actions: transaction.take_actions(), + }; Ok(Json(models::ConstructionParseResponse { account_identifier_signers, diff --git a/core/primitives/benches/serialization.rs b/core/primitives/benches/serialization.rs index c00064c6516..4ec41c1ccfd 100644 --- a/core/primitives/benches/serialization.rs +++ b/core/primitives/benches/serialization.rs @@ -11,7 +11,9 @@ use near_primitives::block::{genesis_chunks, Block}; use near_primitives::hash::CryptoHash; use near_primitives::merkle::combine_hash; use near_primitives::test_utils::account_new; -use near_primitives::transaction::{Action, SignedTransaction, Transaction, TransferAction}; +use near_primitives::transaction::{ + Action, SignedTransaction, Transaction, TransactionV0, TransferAction, +}; use near_primitives::types::{EpochId, StateRoot}; use near_primitives::validator_signer::InMemoryValidatorSigner; use near_primitives::version::PROTOCOL_VERSION; @@ -25,14 +27,14 @@ fn create_transaction() -> SignedTransaction { } SignedTransaction::new( Signature::empty(KeyType::ED25519), - Transaction { + Transaction::V0(TransactionV0 { signer_id: "123213123123".parse().unwrap(), public_key: PublicKey::empty(KeyType::ED25519), nonce: 123, receiver_id: "1231231232131".parse().unwrap(), block_hash: Default::default(), actions, - }, + }), ) } diff --git a/core/primitives/src/errors.rs b/core/primitives/src/errors.rs index 4ace4cee370..5f0f5e7715a 100644 --- a/core/primitives/src/errors.rs +++ b/core/primitives/src/errors.rs @@ -178,6 +178,8 @@ pub enum InvalidTxError { ActionsValidation(ActionsValidationError), /// The size of serialized transaction exceeded the limit. TransactionSizeExceeded { size: u64, limit: u64 }, + /// Transaction version is invalid. + InvalidTransactionVersion, } impl std::error::Error for InvalidTxError {} @@ -573,6 +575,9 @@ impl Display for InvalidTxError { InvalidTxError::TransactionSizeExceeded { size, limit } => { write!(f, "Size of serialized transaction {} exceeded the limit {}", size, limit) } + InvalidTxError::InvalidTransactionVersion => { + write!(f, "Transaction version is invalid") + } } } } diff --git a/core/primitives/src/receipt.rs b/core/primitives/src/receipt.rs index a6230d45e73..293e1728a2b 100644 --- a/core/primitives/src/receipt.rs +++ b/core/primitives/src/receipt.rs @@ -10,6 +10,8 @@ use serde_with::serde_as; use std::borrow::Borrow; use std::collections::{BTreeMap, HashMap}; use std::fmt; +use std::io::{self, Read}; +use std::io::{Error, ErrorKind}; /// The outgoing (egress) data which will be transformed /// to a `DataReceipt` to be sent to a `receipt.receiver` @@ -41,7 +43,7 @@ pub struct DataReceiver { serde::Serialize, serde::Deserialize, )] -pub struct Receipt { +pub struct ReceiptV0 { /// An issuer account_id of a particular receipt. /// `predecessor_id` could be either `Transaction` `signer_id` or intermediate contract's `account_id`. pub predecessor_id: AccountId, @@ -53,34 +55,243 @@ pub struct Receipt { pub receipt: ReceiptEnum, } +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct ReceiptV1 { + /// An issuer account_id of a particular receipt. + /// `predecessor_id` could be either `Transaction` `signer_id` or intermediate contract's `account_id`. + pub predecessor_id: AccountId, + /// `receiver_id` is a receipt destination. + pub receiver_id: AccountId, + /// An unique id for the receipt + pub receipt_id: CryptoHash, + /// A receipt type + pub receipt: ReceiptEnum, + /// Priority of a receipt + pub priority: u64, +} + +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum Receipt { + V0(ReceiptV0), + V1(ReceiptV1), +} + +impl BorshSerialize for Receipt { + fn serialize(&self, writer: &mut W) -> io::Result<()> { + match self { + Receipt::V0(receipt) => receipt.serialize(writer), + Receipt::V1(receipt) => { + BorshSerialize::serialize(&1_u8, writer)?; + receipt.serialize(writer) + } + } + } +} + +impl BorshDeserialize for Receipt { + /// Deserialize based on the first and second bytes of the stream. For V0, we do backward compatible deserialization by deserializing + /// the entire stream into V0. For V1, we consume the first byte and then deserialize the rest. + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let u1 = u8::deserialize_reader(reader)?; + let u2 = u8::deserialize_reader(reader)?; + let u3 = u8::deserialize_reader(reader)?; + let u4 = u8::deserialize_reader(reader)?; + // This is a ridiculous hackery: because the first field in `ReceiptV0` is an `AccountId` + // and an account id is at most 64 bytes, for all valid `ReceiptV0` the second byte must be 0 + // because of the littel endian encoding of the length of the account id. + // On the other hand, for `ReceiptV0`, since the first byte is 1 and an account id must have nonzero + // length, so the second byte must not be zero. Therefore, we can distinguish between the two versions + // by looking at the second byte. + + let read_predecessor_id = |buf: [u8; 4], reader: &mut R| -> std::io::Result { + let str_len = u32::from_le_bytes(buf); + let mut str_vec = Vec::with_capacity(str_len as usize); + for _ in 0..str_len { + str_vec.push(u8::deserialize_reader(reader)?); + } + AccountId::try_from(String::from_utf8(str_vec).map_err(|_| { + Error::new(ErrorKind::InvalidData, "Failed to parse AccountId from bytes") + })?) + .map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string())) + }; + + if u2 == 0 { + let signer_id = read_predecessor_id([u1, u2, u3, u4], reader)?; + let receiver_id = AccountId::deserialize_reader(reader)?; + let receipt_id = CryptoHash::deserialize_reader(reader)?; + let receipt = ReceiptEnum::deserialize_reader(reader)?; + Ok(Receipt::V0(ReceiptV0 { + predecessor_id: signer_id, + receiver_id, + receipt_id, + receipt, + })) + } else { + let u5 = u8::deserialize_reader(reader)?; + let signer_id = read_predecessor_id([u2, u3, u4, u5], reader)?; + let receiver_id = AccountId::deserialize_reader(reader)?; + let receipt_id = CryptoHash::deserialize_reader(reader)?; + let receipt = ReceiptEnum::deserialize_reader(reader)?; + let priority = u64::deserialize_reader(reader)?; + Ok(Receipt::V1(ReceiptV1 { + predecessor_id: signer_id, + receiver_id, + receipt_id, + receipt, + priority, + })) + } + } +} + +pub enum ReceiptPriority { + /// Used in ReceiptV1 + Priority(u64), + /// Used in ReceiptV0 + NoPriority, +} + +impl ReceiptPriority { + pub fn value(&self) -> u64 { + match self { + ReceiptPriority::Priority(value) => *value, + ReceiptPriority::NoPriority => 0, + } + } +} + impl Borrow for Receipt { fn borrow(&self) -> &CryptoHash { - &self.receipt_id + match self { + Receipt::V0(receipt) => &receipt.receipt_id, + Receipt::V1(receipt) => &receipt.receipt_id, + } } } impl Receipt { + pub fn receiver_id(&self) -> &AccountId { + match self { + Receipt::V0(receipt) => &receipt.receiver_id, + Receipt::V1(receipt) => &receipt.receiver_id, + } + } + + pub fn set_receiver_id(&mut self, receiver_id: AccountId) { + match self { + Receipt::V0(receipt) => receipt.receiver_id = receiver_id, + Receipt::V1(receipt) => receipt.receiver_id = receiver_id, + } + } + + pub fn predecessor_id(&self) -> &AccountId { + match self { + Receipt::V0(receipt) => &receipt.predecessor_id, + Receipt::V1(receipt) => &receipt.predecessor_id, + } + } + + pub fn set_predecessor_id(&mut self, predecessor_id: AccountId) { + match self { + Receipt::V0(receipt) => receipt.predecessor_id = predecessor_id, + Receipt::V1(receipt) => receipt.predecessor_id = predecessor_id, + } + } + + pub fn receipt(&self) -> &ReceiptEnum { + match self { + Receipt::V0(receipt) => &receipt.receipt, + Receipt::V1(receipt) => &receipt.receipt, + } + } + + pub fn receipt_mut(&mut self) -> &mut ReceiptEnum { + match self { + Receipt::V0(receipt) => &mut receipt.receipt, + Receipt::V1(receipt) => &mut receipt.receipt, + } + } + + pub fn take_receipt(self) -> ReceiptEnum { + match self { + Receipt::V0(receipt) => receipt.receipt, + Receipt::V1(receipt) => receipt.receipt, + } + } + + pub fn receipt_id(&self) -> &CryptoHash { + match self { + Receipt::V0(receipt) => &receipt.receipt_id, + Receipt::V1(receipt) => &receipt.receipt_id, + } + } + + pub fn set_receipt_id(&mut self, receipt_id: CryptoHash) { + match self { + Receipt::V0(receipt) => receipt.receipt_id = receipt_id, + Receipt::V1(receipt) => receipt.receipt_id = receipt_id, + } + } + + pub fn priority(&self) -> ReceiptPriority { + match self { + Receipt::V0(_) => ReceiptPriority::NoPriority, + Receipt::V1(receipt) => ReceiptPriority::Priority(receipt.priority), + } + } + /// It's not a content hash, but receipt_id is unique. pub fn get_hash(&self) -> CryptoHash { - self.receipt_id + *self.receipt_id() } /// Generates a receipt with a transfer from system for a given balance without a receipt_id. - /// This should be used for token refunds instead of gas refunds. It doesn't refund the - /// allowance of the access key. For gas refunds use `new_gas_refund`. - pub fn new_balance_refund(receiver_id: &AccountId, refund: Balance) -> Self { - Receipt { - predecessor_id: "system".parse().unwrap(), - receiver_id: receiver_id.clone(), - receipt_id: CryptoHash::default(), + /// This should be used for token refunds instead of gas refunds. It inherits priority from the parent receipt. + /// It doesn't refund the allowance of the access key. For gas refunds use `new_gas_refund`. + pub fn new_balance_refund( + receiver_id: &AccountId, + refund: Balance, + priority: ReceiptPriority, + ) -> Self { + match priority { + ReceiptPriority::Priority(priority) => Receipt::V1(ReceiptV1 { + predecessor_id: "system".parse().unwrap(), + receiver_id: receiver_id.clone(), + receipt_id: CryptoHash::default(), - receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: "system".parse().unwrap(), - signer_public_key: PublicKey::empty(KeyType::ED25519), - gas_price: 0, - output_data_receivers: vec![], - input_data_ids: vec![], - actions: vec![Action::Transfer(TransferAction { deposit: refund })], + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: "system".parse().unwrap(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: refund })], + }), + priority, + }), + ReceiptPriority::NoPriority => Receipt::V0(ReceiptV0 { + predecessor_id: "system".parse().unwrap(), + receiver_id: receiver_id.clone(), + receipt_id: CryptoHash::default(), + + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: "system".parse().unwrap(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: refund })], + }), }), } } @@ -89,25 +300,44 @@ impl Receipt { /// receipt_id. It contains `signer_id` and `signer_public_key` to indicate this is a gas /// refund. The execution of this receipt will try to refund the allowance of the /// access key with the given public key. + /// Gas refund does not inherit priority from its parent receipt and has no priority associated with it /// NOTE: The access key may be replaced by the owner, so the execution can't rely that the /// access key is the same and it should use best effort for the refund. pub fn new_gas_refund( receiver_id: &AccountId, refund: Balance, signer_public_key: PublicKey, + priority: ReceiptPriority, ) -> Self { - Receipt { - predecessor_id: "system".parse().unwrap(), - receiver_id: receiver_id.clone(), - receipt_id: CryptoHash::default(), + match priority { + ReceiptPriority::Priority(priority) => Receipt::V1(ReceiptV1 { + predecessor_id: "system".parse().unwrap(), + receiver_id: receiver_id.clone(), + receipt_id: CryptoHash::default(), - receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: receiver_id.clone(), - signer_public_key, - gas_price: 0, - output_data_receivers: vec![], - input_data_ids: vec![], - actions: vec![Action::Transfer(TransferAction { deposit: refund })], + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: receiver_id.clone(), + signer_public_key, + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: refund })], + }), + priority, + }), + ReceiptPriority::NoPriority => Receipt::V0(ReceiptV0 { + predecessor_id: "system".parse().unwrap(), + receiver_id: receiver_id.clone(), + receipt_id: CryptoHash::default(), + + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: receiver_id.clone(), + signer_public_key, + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: refund })], + }), }), } } @@ -291,3 +521,49 @@ pub struct BufferedReceiptIndices { /// Map of shard to list of receipts to send to it. pub type ReceiptResult = HashMap>; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_receipt_v0_serialization() { + let receipt_v0 = Receipt::V0(ReceiptV0 { + predecessor_id: "predecessor_id".parse().unwrap(), + receiver_id: "receiver_id".parse().unwrap(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: "signer_id".parse().unwrap(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: 0 })], + }), + }); + let serialized_receipt = borsh::to_vec(&receipt_v0).unwrap(); + let receipt2 = Receipt::try_from_slice(&serialized_receipt).unwrap(); + assert_eq!(receipt_v0, receipt2); + } + + #[test] + fn test_receipt_v1_serialization() { + let receipt_v1 = Receipt::V1(ReceiptV1 { + predecessor_id: "predecessor_id".parse().unwrap(), + receiver_id: "receiver_id".parse().unwrap(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: "signer_id".parse().unwrap(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: 0 })], + }), + priority: 1, + }); + let serialized_receipt = borsh::to_vec(&receipt_v1).unwrap(); + let receipt2 = Receipt::try_from_slice(&serialized_receipt).unwrap(); + assert_eq!(receipt_v1, receipt2); + } +} diff --git a/core/primitives/src/state_record.rs b/core/primitives/src/state_record.rs index 3b62a361906..1dab335f550 100644 --- a/core/primitives/src/state_record.rs +++ b/core/primitives/src/state_record.rs @@ -179,7 +179,7 @@ pub fn state_record_to_account_id(state_record: &StateRecord) -> &AccountId { | StateRecord::ReceivedData { account_id, .. } | StateRecord::Data { account_id, .. } => account_id, StateRecord::PostponedReceipt(receipt) | StateRecord::DelayedReceipt(receipt) => { - &receipt.receiver_id + receipt.receiver_id() } } } diff --git a/core/primitives/src/test_utils.rs b/core/primitives/src/test_utils.rs index fb3f4ce3eae..e488cc7cfa5 100644 --- a/core/primitives/src/test_utils.rs +++ b/core/primitives/src/test_utils.rs @@ -11,7 +11,7 @@ use crate::sharding::{ShardChunkHeader, ShardChunkHeaderV3}; use crate::transaction::{ Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction, FunctionCallAction, SignedTransaction, StakeAction, Transaction, - TransferAction, + TransactionV0, TransactionV1, TransferAction, }; use crate::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas, Nonce}; use crate::validator_signer::{InMemoryValidatorSigner, ValidatorSigner}; @@ -36,8 +36,31 @@ impl Transaction { receiver_id: AccountId, nonce: Nonce, block_hash: CryptoHash, + priority_fee: u64, ) -> Self { - Self { signer_id, public_key, nonce, receiver_id, block_hash, actions: vec![] } + Transaction::V1(TransactionV1 { + signer_id, + public_key, + nonce, + receiver_id, + block_hash, + actions: vec![], + priority_fee, + }) + } + + pub fn actions_mut(&mut self) -> &mut Vec { + match self { + Transaction::V0(tx) => &mut tx.actions, + Transaction::V1(tx) => &mut tx.actions, + } + } + + pub fn nonce_mut(&mut self) -> &mut Nonce { + match self { + Transaction::V0(tx) => &mut tx.nonce, + Transaction::V1(tx) => &mut tx.nonce, + } } pub fn sign(self, signer: &dyn Signer) -> SignedTransaction { @@ -46,12 +69,12 @@ impl Transaction { } pub fn create_account(mut self) -> Self { - self.actions.push(Action::CreateAccount(CreateAccountAction {})); + self.actions_mut().push(Action::CreateAccount(CreateAccountAction {})); self } pub fn deploy_contract(mut self, code: Vec) -> Self { - self.actions.push(Action::DeployContract(DeployContractAction { code })); + self.actions_mut().push(Action::DeployContract(DeployContractAction { code })); self } @@ -62,7 +85,7 @@ impl Transaction { gas: Gas, deposit: Balance, ) -> Self { - self.actions.push(Action::FunctionCall(Box::new(FunctionCallAction { + self.actions_mut().push(Action::FunctionCall(Box::new(FunctionCallAction { method_name, args, gas, @@ -72,31 +95,34 @@ impl Transaction { } pub fn transfer(mut self, deposit: Balance) -> Self { - self.actions.push(Action::Transfer(TransferAction { deposit })); + self.actions_mut().push(Action::Transfer(TransferAction { deposit })); self } pub fn stake(mut self, stake: Balance, public_key: PublicKey) -> Self { - self.actions.push(Action::Stake(Box::new(StakeAction { stake, public_key }))); + self.actions_mut().push(Action::Stake(Box::new(StakeAction { stake, public_key }))); self } pub fn add_key(mut self, public_key: PublicKey, access_key: AccessKey) -> Self { - self.actions.push(Action::AddKey(Box::new(AddKeyAction { public_key, access_key }))); + self.actions_mut().push(Action::AddKey(Box::new(AddKeyAction { public_key, access_key }))); self } pub fn delete_key(mut self, public_key: PublicKey) -> Self { - self.actions.push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); + self.actions_mut().push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); self } pub fn delete_account(mut self, beneficiary_id: AccountId) -> Self { - self.actions.push(Action::DeleteAccount(DeleteAccountAction { beneficiary_id })); + self.actions_mut().push(Action::DeleteAccount(DeleteAccountAction { beneficiary_id })); self } } +/// This block implements a set of helper functions to create transactions for testing purposes. impl SignedTransaction { + /// Creates v0 for now because v1 is prohibited in the protocol. + /// Once v1 is allowed, this function should be updated to create v1 transactions. pub fn from_actions( nonce: Nonce, signer_id: AccountId, @@ -104,15 +130,38 @@ impl SignedTransaction { signer: &dyn Signer, actions: Vec, block_hash: CryptoHash, + _priority_fee: u64, ) -> Self { - Transaction { + Transaction::V0(TransactionV0 { nonce, signer_id, public_key: signer.public_key(), receiver_id, block_hash, actions, - } + }) + .sign(signer) + } + + /// Explicitly create v1 transaction to test in cases where errors are expected. + pub fn from_actions_v1( + nonce: Nonce, + signer_id: AccountId, + receiver_id: AccountId, + signer: &dyn Signer, + actions: Vec, + block_hash: CryptoHash, + priority_fee: u64, + ) -> Self { + Transaction::V1(TransactionV1 { + nonce, + signer_id, + public_key: signer.public_key(), + receiver_id, + block_hash, + actions, + priority_fee, + }) .sign(signer) } @@ -131,6 +180,7 @@ impl SignedTransaction { signer, vec![Action::Transfer(TransferAction { deposit })], block_hash, + 0, ) } @@ -149,6 +199,7 @@ impl SignedTransaction { signer, vec![Action::Stake(Box::new(StakeAction { stake, public_key }))], block_hash, + 0, ) } @@ -175,6 +226,7 @@ impl SignedTransaction { Action::Transfer(TransferAction { deposit: amount }), ], block_hash, + 0, ) } @@ -203,6 +255,7 @@ impl SignedTransaction { Action::DeployContract(DeployContractAction { code }), ], block_hash, + 0, ) } @@ -229,6 +282,7 @@ impl SignedTransaction { deposit, }))], block_hash, + 0, ) } @@ -247,6 +301,7 @@ impl SignedTransaction { signer, vec![Action::DeleteAccount(DeleteAccountAction { beneficiary_id })], block_hash, + 0, ) } @@ -258,6 +313,7 @@ impl SignedTransaction { &EmptySigner {}, vec![], block_hash, + 0, ) } } diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs index 1a7759c95f9..c0a1b15c13f 100644 --- a/core/primitives/src/transaction.rs +++ b/core/primitives/src/transaction.rs @@ -13,6 +13,7 @@ use serde::ser::Error as EncodeError; use std::borrow::Borrow; use std::fmt; use std::hash::{Hash, Hasher}; +use std::io::{Error, ErrorKind, Read, Write}; #[cfg(feature = "protocol_feature_nonrefundable_transfer_nep491")] pub use crate::action::NonrefundableStorageTransferAction; @@ -24,7 +25,7 @@ pub use crate::action::{ pub type LogEntry = String; #[derive(BorshSerialize, BorshDeserialize, serde::Serialize, PartialEq, Eq, Debug, Clone)] -pub struct Transaction { +pub struct TransactionV0 { /// An account on which behalf transaction is signed pub signer_id: AccountId, /// A public key of the access key which was used to sign an account. @@ -41,6 +42,26 @@ pub struct Transaction { pub actions: Vec, } +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub struct TransactionV1 { + /// An account on which behalf transaction is signed + pub signer_id: AccountId, + /// A public key of the access key which was used to sign an account. + /// Access key holds permissions for calling certain kinds of actions. + pub public_key: PublicKey, + /// Nonce is used to determine order of transaction in the pool. + /// It increments for a combination of `signer_id` and `public_key` + pub nonce: Nonce, + /// Receiver account for this transaction + pub receiver_id: AccountId, + /// The hash of the block in the blockchain on top of which the given transaction is valid + pub block_hash: CryptoHash, + /// A list of actions to be applied + pub actions: Vec, + /// Priority fee. Unit is 10^12 yotcoNEAR + pub priority_fee: u64, +} + impl Transaction { /// Computes a hash of the transaction for signing and size of serialized transaction pub fn get_hash_and_size(&self) -> (CryptoHash, u64) { @@ -49,6 +70,147 @@ impl Transaction { } } +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum Transaction { + V0(TransactionV0), + V1(TransactionV1), +} + +impl Transaction { + pub fn signer_id(&self) -> &AccountId { + match self { + Transaction::V0(tx) => &tx.signer_id, + Transaction::V1(tx) => &tx.signer_id, + } + } + + pub fn receiver_id(&self) -> &AccountId { + match self { + Transaction::V0(tx) => &tx.receiver_id, + Transaction::V1(tx) => &tx.receiver_id, + } + } + + pub fn public_key(&self) -> &PublicKey { + match self { + Transaction::V0(tx) => &tx.public_key, + Transaction::V1(tx) => &tx.public_key, + } + } + + pub fn nonce(&self) -> Nonce { + match self { + Transaction::V0(tx) => tx.nonce, + Transaction::V1(tx) => tx.nonce, + } + } + + pub fn actions(&self) -> &[Action] { + match self { + Transaction::V0(tx) => &tx.actions, + Transaction::V1(tx) => &tx.actions, + } + } + + pub fn take_actions(self) -> Vec { + match self { + Transaction::V0(tx) => tx.actions, + Transaction::V1(tx) => tx.actions, + } + } + + pub fn block_hash(&self) -> &CryptoHash { + match self { + Transaction::V0(tx) => &tx.block_hash, + Transaction::V1(tx) => &tx.block_hash, + } + } + + pub fn priority_fee(&self) -> Option { + match self { + Transaction::V0(_) => None, + Transaction::V1(tx) => Some(tx.priority_fee), + } + } +} + +impl BorshSerialize for Transaction { + fn serialize(&self, writer: &mut W) -> Result<(), Error> { + match self { + Transaction::V0(tx) => tx.serialize(writer)?, + Transaction::V1(tx) => { + BorshSerialize::serialize(&1_u8, writer)?; + tx.serialize(writer)?; + } + } + Ok(()) + } +} + +impl BorshDeserialize for Transaction { + /// Deserialize based on the first and second bytes of the stream. For V0, we do backward compatible deserialization by deserializing + /// the entire stream into V0. For V1, we consume the first byte and then deserialize the rest. + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let u1 = u8::deserialize_reader(reader)?; + let u2 = u8::deserialize_reader(reader)?; + let u3 = u8::deserialize_reader(reader)?; + let u4 = u8::deserialize_reader(reader)?; + // This is a ridiculous hackery: because the first field in `TransactionV0` is an `AccountId` + // and an account id is at most 64 bytes, for all valid `TransactionV0` the second byte must be 0 + // because of the littel endian encoding of the length of the account id. + // On the other hand, for `TransactionV1`, since the first byte is 1 and an account id must have nonzero + // length, so the second byte must not be zero. Therefore, we can distinguish between the two versions + // by looking at the second byte. + + let read_signer_id = |buf: [u8; 4], reader: &mut R| -> std::io::Result { + let str_len = u32::from_le_bytes(buf); + let mut str_vec = Vec::with_capacity(str_len as usize); + for _ in 0..str_len { + str_vec.push(u8::deserialize_reader(reader)?); + } + AccountId::try_from(String::from_utf8(str_vec).map_err(|_| { + Error::new(ErrorKind::InvalidData, "Failed to parse AccountId from bytes") + })?) + .map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string())) + }; + + if u2 == 0 { + let signer_id = read_signer_id([u1, u2, u3, u4], reader)?; + let public_key = PublicKey::deserialize_reader(reader)?; + let nonce = Nonce::deserialize_reader(reader)?; + let receiver_id = AccountId::deserialize_reader(reader)?; + let block_hash = CryptoHash::deserialize_reader(reader)?; + let actions = Vec::::deserialize_reader(reader)?; + Ok(Transaction::V0(TransactionV0 { + signer_id, + public_key, + nonce, + receiver_id, + block_hash, + actions, + })) + } else { + let u5 = u8::deserialize_reader(reader)?; + let signer_id = read_signer_id([u2, u3, u4, u5], reader)?; + let public_key = PublicKey::deserialize_reader(reader)?; + let nonce = Nonce::deserialize_reader(reader)?; + let receiver_id = AccountId::deserialize_reader(reader)?; + let block_hash = CryptoHash::deserialize_reader(reader)?; + let actions = Vec::::deserialize_reader(reader)?; + let priority_fee = u64::deserialize_reader(reader)?; + Ok(Transaction::V1(TransactionV1 { + signer_id, + public_key, + nonce, + receiver_id, + block_hash, + actions, + priority_fee, + })) + } + } +} + #[derive(BorshSerialize, BorshDeserialize, Eq, Debug, Clone)] #[borsh(init=init)] pub struct SignedTransaction { @@ -319,14 +481,14 @@ mod tests { #[test] fn test_verify_transaction() { let signer = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); - let transaction = Transaction { + let transaction = Transaction::V0(TransactionV0 { signer_id: "test".parse().unwrap(), public_key: signer.public_key(), nonce: 0, receiver_id: "test".parse().unwrap(), block_hash: Default::default(), actions: vec![], - } + }) .sign(&signer); let wrong_public_key = PublicKey::from_seed(KeyType::ED25519, "wrong"); let valid_keys = vec![signer.public_key(), wrong_public_key.clone()]; @@ -340,12 +502,9 @@ mod tests { assert!(verify_transaction_signature(&decoded_tx, &valid_keys)); } - /// This test is change checker for a reason - we don't expect transaction format to change. - /// If it does - you MUST update all of the dependencies: like nearlib and other clients. - #[test] - fn test_serialize_transaction() { + fn create_transaction_v0() -> TransactionV0 { let public_key: PublicKey = "22skMptHjFWNyuEWY22ftn2AbLPSYpmYwGJRGwpNHbTV".parse().unwrap(); - let transaction = Transaction { + TransactionV0 { signer_id: "test.near".parse().unwrap(), public_key: public_key.clone(), nonce: 1, @@ -381,7 +540,56 @@ mod tests { beneficiary_id: "123".parse().unwrap(), }), ], - }; + } + } + + fn create_transaction_v1() -> TransactionV1 { + let public_key: PublicKey = "22skMptHjFWNyuEWY22ftn2AbLPSYpmYwGJRGwpNHbTV".parse().unwrap(); + TransactionV1 { + signer_id: "test.near".parse().unwrap(), + public_key: public_key.clone(), + nonce: 1, + receiver_id: "123".parse().unwrap(), + block_hash: Default::default(), + actions: vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::DeployContract(DeployContractAction { code: vec![1, 2, 3] }), + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "qqq".to_string(), + args: vec![1, 2, 3], + gas: 1_000, + deposit: 1_000_000, + })), + Action::Transfer(TransferAction { deposit: 123 }), + Action::Stake(Box::new(StakeAction { + public_key: public_key.clone(), + stake: 1_000_000, + })), + Action::AddKey(Box::new(AddKeyAction { + public_key: public_key.clone(), + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: "zzz".parse().unwrap(), + method_names: vec!["www".to_string()], + }), + }, + })), + Action::DeleteKey(Box::new(DeleteKeyAction { public_key })), + Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: "123".parse().unwrap(), + }), + ], + priority_fee: 1, + } + } + + /// This test is change checker for a reason - we don't expect transaction format to change. + /// If it does - you MUST update all of the dependencies: like nearlib and other clients. + #[test] + fn test_serialize_transaction() { + let transaction = Transaction::V0(create_transaction_v0()); let signed_tx = SignedTransaction::new(Signature::empty(KeyType::ED25519), transaction); let new_signed_tx = SignedTransaction::try_from_slice(&borsh::to_vec(&signed_tx).unwrap()).unwrap(); @@ -392,6 +600,19 @@ mod tests { ); } + #[test] + fn test_serialize_transaction_versions() { + let transaction_v0 = Transaction::V0(create_transaction_v0()); + let serialized_tx_v0 = borsh::to_vec(&transaction_v0).unwrap(); + let deserialized_tx_v0 = Transaction::try_from_slice(&serialized_tx_v0).unwrap(); + assert_eq!(transaction_v0, deserialized_tx_v0); + + let transaction_v1 = Transaction::V1(create_transaction_v1()); + let serialized_tx_v1 = borsh::to_vec(&transaction_v1).unwrap(); + let deserialized_tx_v1 = Transaction::try_from_slice(&serialized_tx_v1).unwrap(); + assert_eq!(transaction_v1, deserialized_tx_v1); + } + #[test] fn test_outcome_to_hashes() { let outcome = ExecutionOutcome { diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index cd347e43fae..1d873f2d8d0 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -18,7 +18,7 @@ use crate::errors::TxExecutionError; use crate::hash::{hash, CryptoHash}; use crate::merkle::{combine_hash, MerklePath}; use crate::network::PeerId; -use crate::receipt::{ActionReceipt, DataReceipt, DataReceiver, Receipt, ReceiptEnum}; +use crate::receipt::{ActionReceipt, DataReceipt, DataReceiver, Receipt, ReceiptEnum, ReceiptV1}; use crate::serialize::dec_format; use crate::sharding::{ ChunkHash, ShardChunk, ShardChunkHeader, ShardChunkHeaderInner, ShardChunkHeaderInnerV2, @@ -1353,6 +1353,7 @@ pub struct SignedTransactionView { pub nonce: Nonce, pub receiver_id: AccountId, pub actions: Vec, + pub priority_fee: u64, pub signature: Signature, pub hash: CryptoHash, } @@ -1360,19 +1361,17 @@ pub struct SignedTransactionView { impl From for SignedTransactionView { fn from(signed_tx: SignedTransaction) -> Self { let hash = signed_tx.get_hash(); + let transaction = signed_tx.transaction; + let priority_fee = transaction.priority_fee().unwrap_or_default(); SignedTransactionView { - signer_id: signed_tx.transaction.signer_id, - public_key: signed_tx.transaction.public_key, - nonce: signed_tx.transaction.nonce, - receiver_id: signed_tx.transaction.receiver_id, - actions: signed_tx - .transaction - .actions - .into_iter() - .map(|action| action.into()) - .collect(), + signer_id: transaction.signer_id().clone(), + public_key: transaction.public_key().clone(), + nonce: transaction.nonce(), + receiver_id: transaction.receiver_id().clone(), + actions: transaction.take_actions().into_iter().map(|action| action.into()).collect(), signature: signed_tx.signature, hash, + priority_fee, } } } @@ -1933,6 +1932,7 @@ pub struct ReceiptView { pub receipt_id: CryptoHash, pub receipt: ReceiptEnumView, + pub priority: u64, } #[derive( @@ -1991,14 +1991,15 @@ fn default_is_promise() -> bool { impl From for ReceiptView { fn from(receipt: Receipt) -> Self { - let is_promise_yield = matches!(&receipt.receipt, ReceiptEnum::PromiseYield(_)); - let is_promise_resume = matches!(&receipt.receipt, ReceiptEnum::PromiseResume(_)); + let is_promise_yield = matches!(receipt.receipt(), ReceiptEnum::PromiseYield(_)); + let is_promise_resume = matches!(receipt.receipt(), ReceiptEnum::PromiseResume(_)); + let priority = receipt.priority().value(); ReceiptView { - predecessor_id: receipt.predecessor_id, - receiver_id: receipt.receiver_id, - receipt_id: receipt.receipt_id, - receipt: match receipt.receipt { + predecessor_id: receipt.predecessor_id().clone(), + receiver_id: receipt.receiver_id().clone(), + receipt_id: *receipt.receipt_id(), + receipt: match receipt.take_receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { ReceiptEnumView::Action { signer_id: action_receipt.signer_id, @@ -2029,6 +2030,7 @@ impl From for ReceiptView { } } }, + priority, } } } @@ -2037,7 +2039,7 @@ impl TryFrom for Receipt { type Error = Box; fn try_from(receipt_view: ReceiptView) -> Result { - Ok(Receipt { + Ok(Receipt::V1(ReceiptV1 { predecessor_id: receipt_view.predecessor_id, receiver_id: receipt_view.receiver_id, receipt_id: receipt_view.receipt_id, @@ -2085,7 +2087,8 @@ impl TryFrom for Receipt { } } }, - }) + priority: receipt_view.priority, + })) } } diff --git a/core/store/benches/finalize_bench.rs b/core/store/benches/finalize_bench.rs index 63d2e9f6126..fc6d9bb0730 100644 --- a/core/store/benches/finalize_bench.rs +++ b/core/store/benches/finalize_bench.rs @@ -22,7 +22,7 @@ use near_crypto::{InMemorySigner, KeyType, Signer}; use near_primitives::congestion_info::CongestionInfo; use near_primitives::hash::CryptoHash; use near_primitives::merkle::{merklize, MerklePathItem}; -use near_primitives::receipt::{ActionReceipt, DataReceipt, Receipt, ReceiptEnum}; +use near_primitives::receipt::{ActionReceipt, DataReceipt, Receipt, ReceiptEnum, ReceiptV0}; use near_primitives::shard_layout::ShardLayout; use near_primitives::sharding::{ ChunkHash, EncodedShardChunk, PartialEncodedChunk, PartialEncodedChunkPart, @@ -142,7 +142,7 @@ fn create_action_receipt( actions: Vec, input_data_ids: Vec, ) -> Receipt { - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: account_id.clone(), receiver_id: account_id.clone(), receipt_id: CryptoHash::hash_borsh(actions.clone()), @@ -154,16 +154,16 @@ fn create_action_receipt( input_data_ids, actions, }), - } + }) } fn create_data_receipt(account_id: &AccountId, data_id: CryptoHash, data_size: usize) -> Receipt { - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: account_id.clone(), receiver_id: account_id.clone(), receipt_id: CryptoHash::hash_borsh(data_id), receipt: ReceiptEnum::Data(DataReceipt { data_id, data: Some(vec![77u8; data_size]) }), - } + }) } fn create_shard_chunk( diff --git a/core/store/src/genesis/state_applier.rs b/core/store/src/genesis/state_applier.rs index 4a680bfc020..e0c4a187883 100644 --- a/core/store/src/genesis/state_applier.rs +++ b/core/store/src/genesis/state_applier.rs @@ -269,11 +269,11 @@ impl GenesisStateApplier { "processing postponed receipts…" ); for receipt in postponed_receipts { - let account_id = &receipt.receiver_id; + let account_id = receipt.receiver_id(); // Logic similar to `apply_receipt` - match receipt.receipt { - ReceiptEnum::Action(ref action_receipt) => { + match receipt.receipt() { + ReceiptEnum::Action(action_receipt) => { let mut pending_data_count: u32 = 0; for data_id in &action_receipt.input_data_ids { storage.modify(|state_update| { @@ -287,7 +287,7 @@ impl GenesisStateApplier { receiver_id: account_id.clone(), data_id: *data_id, }, - &receipt.receipt_id, + receipt.receipt_id(), ) } }); @@ -300,7 +300,7 @@ impl GenesisStateApplier { state_update, TrieKey::PendingDataCount { receiver_id: account_id.clone(), - receipt_id: receipt.receipt_id, + receipt_id: *receipt.receipt_id(), }, &pending_data_count, ); diff --git a/core/store/src/lib.rs b/core/store/src/lib.rs index a0c6613a532..234b537b05f 100644 --- a/core/store/src/lib.rs +++ b/core/store/src/lib.rs @@ -782,10 +782,10 @@ pub fn has_received_data( } pub fn set_postponed_receipt(state_update: &mut TrieUpdate, receipt: &Receipt) { - assert!(matches!(receipt.receipt, ReceiptEnum::Action(_))); + assert!(matches!(receipt.receipt(), ReceiptEnum::Action(_))); let key = TrieKey::PostponedReceipt { - receiver_id: receipt.receiver_id.clone(), - receipt_id: receipt.receipt_id, + receiver_id: receipt.receiver_id().clone(), + receipt_id: *receipt.receipt_id(), }; set(state_update, key, receipt); } @@ -862,11 +862,11 @@ pub fn enqueue_promise_yield_timeout( } pub fn set_promise_yield_receipt(state_update: &mut TrieUpdate, receipt: &Receipt) { - match &receipt.receipt { + match receipt.receipt() { ReceiptEnum::PromiseYield(ref action_receipt) => { assert!(action_receipt.input_data_ids.len() == 1); let key = TrieKey::PromiseYieldReceipt { - receiver_id: receipt.receiver_id.clone(), + receiver_id: receipt.receiver_id().clone(), data_id: action_receipt.input_data_ids[0], }; set(state_update, key, receipt); diff --git a/core/store/src/test_utils.rs b/core/store/src/test_utils.rs index 3aab48d8d6f..694f4729331 100644 --- a/core/store/src/test_utils.rs +++ b/core/store/src/test_utils.rs @@ -10,7 +10,7 @@ use crate::{ use itertools::Itertools; use near_primitives::account::id::AccountId; use near_primitives::hash::CryptoHash; -use near_primitives::receipt::{DataReceipt, PromiseYieldTimeout, Receipt, ReceiptEnum}; +use near_primitives::receipt::{DataReceipt, PromiseYieldTimeout, Receipt, ReceiptEnum, ReceiptV1}; use near_primitives::shard_layout::{ShardUId, ShardVersion}; use near_primitives::state::FlatStateValue; use near_primitives::trie_key::TrieKey; @@ -266,11 +266,17 @@ pub fn gen_receipts(rng: &mut impl Rng, max_size: usize) -> Vec { let accounts = gen_accounts_from_alphabet(rng, 1, max_size, &alphabet); accounts .iter() - .map(|account_id| Receipt { - predecessor_id: account_id.clone(), - receiver_id: account_id.clone(), - receipt_id: CryptoHash::default(), - receipt: ReceiptEnum::Data(DataReceipt { data_id: CryptoHash::default(), data: None }), + .map(|account_id| { + Receipt::V1(ReceiptV1 { + predecessor_id: account_id.clone(), + receiver_id: account_id.clone(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Data(DataReceipt { + data_id: CryptoHash::default(), + data: None, + }), + priority: 0, + }) }) .collect() } diff --git a/core/store/src/trie/resharding.rs b/core/store/src/trie/resharding.rs index 9892a65c4da..2f8e886e519 100644 --- a/core/store/src/trie/resharding.rs +++ b/core/store/src/trie/resharding.rs @@ -266,11 +266,11 @@ fn apply_delayed_receipts_to_children_states_impl( } for receipt in insert_receipts { - let new_shard_uid: ShardUId = account_id_to_shard_uid(&receipt.receiver_id); + let new_shard_uid: ShardUId = account_id_to_shard_uid(receipt.receiver_id()); if !trie_updates.contains_key(&new_shard_uid) { let err = format!( "Account {} is in new shard {:?} but state_roots only contains {:?}", - receipt.receiver_id, + receipt.receiver_id(), new_shard_uid, trie_updates.keys(), ); @@ -295,11 +295,11 @@ fn apply_delayed_receipts_to_children_states_impl( } for receipt in delete_receipts { - let new_shard_uid: ShardUId = account_id_to_shard_uid(&receipt.receiver_id); + let new_shard_uid: ShardUId = account_id_to_shard_uid(receipt.receiver_id()); if !trie_updates.contains_key(&new_shard_uid) { let err = format!( "Account {} is in new shard {:?} but state_roots only contains {:?}", - receipt.receiver_id, + receipt.receiver_id(), new_shard_uid, trie_updates.keys(), ); @@ -706,7 +706,7 @@ mod tests { let mut expected_receipts_by_shard: HashMap<_, _> = state_roots.iter().map(|(shard_uid, _)| (shard_uid, vec![])).collect(); for receipt in expected_all_receipts { - let shard_uid = account_id_to_shard_id(&receipt.receiver_id); + let shard_uid = account_id_to_shard_id(receipt.receiver_id()); expected_receipts_by_shard.get_mut(&shard_uid).unwrap().push(receipt.clone()); } assert_eq!(expected_receipts_by_shard, receipts_by_shard); diff --git a/genesis-tools/genesis-csv-to-json/src/csv_parser.rs b/genesis-tools/genesis-csv-to-json/src/csv_parser.rs index 4a16c517e59..e717877b162 100644 --- a/genesis-tools/genesis-csv-to-json/src/csv_parser.rs +++ b/genesis-tools/genesis-csv-to-json/src/csv_parser.rs @@ -6,6 +6,7 @@ use near_crypto::{KeyType, PublicKey}; use near_network::types::PeerInfo; use near_primitives::account::{AccessKey, AccessKeyPermission, Account, FunctionCallPermission}; use near_primitives::hash::{hash, CryptoHash}; +use near_primitives::receipt::ReceiptV0; use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; use near_primitives::state_record::StateRecord; use near_primitives::transaction::{Action, FunctionCallAction}; @@ -253,7 +254,7 @@ fn account_records(row: &Row, gas_price: Balance) -> Vec { } _ => unimplemented!(), }; - let receipt = Receipt { + let receipt = Receipt::V0(ReceiptV0 { predecessor_id: row.account_id.clone(), receiver_id: row.account_id.clone(), // `receipt_id` can be anything as long as it is unique. @@ -273,7 +274,7 @@ fn account_records(row: &Row, gas_price: Balance) -> Vec { deposit: 0, }))], }), - }; + }); res.push(StateRecord::PostponedReceipt(Box::new(receipt))); } res diff --git a/integration-tests/src/tests/client/benchmarks.rs b/integration-tests/src/tests/client/benchmarks.rs index 1d865d4c6ca..ebd1860a628 100644 --- a/integration-tests/src/tests/client/benchmarks.rs +++ b/integration-tests/src/tests/client/benchmarks.rs @@ -41,6 +41,7 @@ fn benchmark_large_chunk_production_time() { &signer, vec![Action::DeployContract(DeployContractAction { code: vec![92; tx_size] })], last_block_hash, + 0, ); assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); } diff --git a/integration-tests/src/tests/client/cold_storage.rs b/integration-tests/src/tests/client/cold_storage.rs index 57655f5c68a..3e625de9af9 100644 --- a/integration-tests/src/tests/client/cold_storage.rs +++ b/integration-tests/src/tests/client/cold_storage.rs @@ -87,7 +87,7 @@ fn create_tx_deploy_contract( let code = near_test_contracts::rs_contract().to_vec(); let action = DeployContractAction { code }; let action = Action::DeployContract(action); - SignedTransaction::from_actions(height, test0(), test0(), signer, vec![action], block_hash) + SignedTransaction::from_actions(height, test0(), test0(), signer, vec![action], block_hash, 0) } fn create_tx_function_call( @@ -101,7 +101,7 @@ fn create_tx_function_call( gas: 100_000_000_000_000, deposit: 0, })); - SignedTransaction::from_actions(nonce, test0(), test0(), signer, vec![action], block_hash) + SignedTransaction::from_actions(nonce, test0(), test0(), signer, vec![action], block_hash, 0) } /// Deploying test contract and calling write_random_value 5 times every block for 4 epochs. diff --git a/integration-tests/src/tests/client/epoch_sync.rs b/integration-tests/src/tests/client/epoch_sync.rs index 95931a702a3..1b5c1f18d2c 100644 --- a/integration-tests/src/tests/client/epoch_sync.rs +++ b/integration-tests/src/tests/client/epoch_sync.rs @@ -48,6 +48,7 @@ fn generate_transactions(last_hash: &CryptoHash, h: BlockHeight) -> Vec Vec usize { } fn receipt_action(receipt: &Receipt) -> &Action { - match &receipt.receipt { + match receipt.receipt() { ReceiptEnum::Action(action_receipt) => &action_receipt.actions[0], _ => panic!("Expected Action receipt"), } diff --git a/integration-tests/src/tests/client/features/wallet_contract.rs b/integration-tests/src/tests/client/features/wallet_contract.rs index 2c3f8e68fab..da5f7a605a6 100644 --- a/integration-tests/src/tests/client/features/wallet_contract.rs +++ b/integration-tests/src/tests/client/features/wallet_contract.rs @@ -212,6 +212,7 @@ fn test_transaction_from_eth_implicit_account_fail() { access_key: AccessKey::full_access(), }))], *block.hash(), + 0, ); let response = env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); @@ -226,6 +227,7 @@ fn test_transaction_from_eth_implicit_account_fail() { ð_implicit_account_signer, vec![Action::DeployContract(DeployContractAction { code: wallet_contract_code })], *block.hash(), + 0, ); let response = env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); @@ -273,6 +275,7 @@ fn test_wallet_contract_interaction() { &relayer_signer.signer, actions, block_hash, + 0, ); height = check_tx_processing(&mut env, signed_transaction, height, blocks_number); @@ -395,6 +398,7 @@ fn create_rlp_execute_tx( &near_signer.signer, actions, block_hash, + 0, ) } diff --git a/integration-tests/src/tests/client/features/yield_timeouts.rs b/integration-tests/src/tests/client/features/yield_timeouts.rs index c3b383ac702..476edb51c36 100644 --- a/integration-tests/src/tests/client/features/yield_timeouts.rs +++ b/integration-tests/src/tests/client/features/yield_timeouts.rs @@ -41,10 +41,10 @@ fn find_yield_data_ids_from_latest_block(env: &TestEnv) -> Vec { .get_outgoing_receipts_for_shard(last_block_hash, shard_id, last_block_height) .unwrap() { - if let PromiseYield(ref action_receipt) = receipt.receipt { + if let PromiseYield(ref action_receipt) = receipt.receipt() { result.push(action_receipt.input_data_ids[0]); } - if let PromiseResume(ref data_receipt) = receipt.receipt { + if let PromiseResume(ref data_receipt) = receipt.receipt() { result.push(data_receipt.data_id); } } @@ -77,6 +77,7 @@ fn prepare_env_with_yield( code: near_test_contracts::nightly_rs_contract().to_vec(), })], *genesis_block.hash(), + 0, ); let tx_hash = tx.get_hash(); assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); @@ -103,6 +104,7 @@ fn prepare_env_with_yield( deposit: 0, }))], *genesis_block.hash(), + 0, ); let yield_tx_hash = yield_transaction.get_hash(); assert_eq!( @@ -145,6 +147,7 @@ fn invoke_yield_resume( deposit: 0, }))], *genesis_block.hash(), + 0, ); let tx_hash = resume_transaction.get_hash(); assert_eq!( @@ -177,6 +180,7 @@ fn create_congestion(env: &mut TestEnv) { deposit: 0, }))], *genesis_block.hash(), + 0, ); tx_hashes.push(signed_transaction.get_hash()); assert_eq!( diff --git a/integration-tests/src/tests/client/features/zero_balance_account.rs b/integration-tests/src/tests/client/features/zero_balance_account.rs index dd5ed8da9ca..432dab19d4d 100644 --- a/integration-tests/src/tests/client/features/zero_balance_account.rs +++ b/integration-tests/src/tests/client/features/zero_balance_account.rs @@ -192,6 +192,7 @@ fn test_zero_balance_account_add_key() { &new_signer, actions, *genesis_block.hash(), + 0, ); assert_eq!(env.clients[0].process_tx(add_key_tx, false, false), ProcessTxResponse::ValidTx); for i in 5..10 { @@ -222,6 +223,7 @@ fn test_zero_balance_account_add_key() { public_key: keys.last().unwrap().clone(), }))], *genesis_block.hash(), + 0, ); assert_eq!(env.clients[0].process_tx(delete_key_tx, false, false), ProcessTxResponse::ValidTx); for i in 10..15 { diff --git a/integration-tests/src/tests/client/process_blocks.rs b/integration-tests/src/tests/client/process_blocks.rs index b0095a002a1..0600828a1d4 100644 --- a/integration-tests/src/tests/client/process_blocks.rs +++ b/integration-tests/src/tests/client/process_blocks.rs @@ -53,7 +53,7 @@ use near_primitives::test_utils::create_test_signer; use near_primitives::test_utils::TestBlockBuilder; use near_primitives::transaction::{ Action, DeployContractAction, ExecutionStatus, FunctionCallAction, SignedTransaction, - Transaction, + Transaction, TransactionV0, }; use near_primitives::trie_key::TrieKey; use near_primitives::types::validator_stake::ValidatorStake; @@ -163,6 +163,7 @@ pub(crate) fn deploy_test_contract_with_protocol_version( &signer, vec![Action::DeployContract(DeployContractAction { code: wasm_code.to_vec() })], *block.hash(), + 0, ); assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); produce_blocks_from_height_with_protocol_version(env, epoch_length, height, protocol_version) @@ -214,6 +215,7 @@ pub(crate) fn prepare_env_with_congestion( code: near_test_contracts::backwards_compatible_rs_contract().to_vec(), })], *genesis_block.hash(), + 0, ); assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); for i in 1..3 { @@ -248,6 +250,7 @@ pub(crate) fn prepare_env_with_congestion( deposit: 0, }))], *genesis_block.hash(), + 0, ); tx_hashes.push(signed_transaction.get_hash()); assert_eq!( @@ -1025,14 +1028,14 @@ fn test_process_invalid_tx() { let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test0"); let tx = SignedTransaction::new( Signature::empty(KeyType::ED25519), - Transaction { + Transaction::V0(TransactionV0 { signer_id: "test".parse().unwrap(), public_key: signer.public_key(), nonce: 0, receiver_id: "test".parse().unwrap(), block_hash: *env.clients[0].chain.genesis().hash(), actions: vec![], - }, + }), ); for i in 1..12 { env.produce_block(0, i); @@ -1043,14 +1046,14 @@ fn test_process_invalid_tx() { ); let tx2 = SignedTransaction::new( Signature::empty(KeyType::ED25519), - Transaction { + Transaction::V0(TransactionV0 { signer_id: "test".parse().unwrap(), public_key: signer.public_key(), nonce: 0, receiver_id: "test".parse().unwrap(), block_hash: hash(&[1]), actions: vec![], - }, + }), ); assert_eq!( env.clients[0].process_tx(tx2, false, false), @@ -2193,6 +2196,7 @@ fn test_validate_chunk_extra() { code: near_test_contracts::rs_contract().to_vec(), })], *genesis_block.hash(), + 0, ); assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); let mut last_block = genesis_block; @@ -2215,6 +2219,7 @@ fn test_validate_chunk_extra() { deposit: 0, }))], *last_block.hash(), + 0, ); assert_eq!( env.clients[0].process_tx(function_call_tx, false, false), @@ -2616,6 +2621,7 @@ fn test_delayed_receipt_count_limit() { &signer, vec![Action::DeployContract(DeployContractAction { code: vec![92; 10000] })], *genesis_block.hash(), + 0, ); assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); } @@ -3293,6 +3299,7 @@ fn test_validator_stake_host_function() { deposit: 0, }))], *genesis_block.hash(), + 0, ); assert_eq!( env.clients[0].process_tx(signed_transaction, false, false), diff --git a/integration-tests/src/tests/client/resharding.rs b/integration-tests/src/tests/client/resharding.rs index 45bc5938a5d..9c71e30be9a 100644 --- a/integration-tests/src/tests/client/resharding.rs +++ b/integration-tests/src/tests/client/resharding.rs @@ -545,7 +545,7 @@ impl TestReshardingEnv { for tx in txs_to_check { let id = &tx.get_hash(); - let signer_account_id = &tx.transaction.signer_id; + let signer_account_id = tx.transaction.signer_id(); let shard_uid = account_id_to_shard_uid(signer_account_id, &shard_layout); tracing::trace!(target: "test", tx=?id, ?signer_account_id, ?shard_uid, "checking tx"); @@ -627,10 +627,10 @@ impl TestReshardingEnv { .clone(); for receipt in outgoing_receipts.iter() { let target_shard_id = - client.chain.get_shard_id_for_receipt_id(&receipt.receipt_id).unwrap(); + client.chain.get_shard_id_for_receipt_id(receipt.receipt_id()).unwrap(); assert_eq!( target_shard_id, - account_id_to_shard_id(&receipt.receiver_id, &shard_layout) + account_id_to_shard_id(receipt.receiver_id(), &shard_layout) ); } } @@ -810,8 +810,8 @@ fn check_outgoing_receipts_reassigned_impl( // In V0->V1 resharding the outgoing receipts should be reassigned // to the receipt receiver's shard id. for receipt in outgoing_receipts { - let receiver = receipt.receiver_id; - let receiver_shard_id = account_id_to_shard_id(&receiver, &shard_layout); + let receiver_shard_id = + account_id_to_shard_id(receipt.receiver_id(), &shard_layout); assert_eq!(receiver_shard_id, shard_id); } } @@ -1285,6 +1285,7 @@ fn setup_test_env_with_cross_contract_txs( &signer, actions, genesis_hash, + 0, ); init_txs.push(tx); } @@ -1458,6 +1459,7 @@ fn gen_cross_contract_tx_impl( deposit: 0, }))], *block_hash, + 0, ) } @@ -1603,6 +1605,7 @@ fn generate_yield_create_tx( deposit: 0, }))], *block_hash, + 0, ) } @@ -1631,6 +1634,7 @@ fn setup_test_env_with_promise_yield_txs( &signer, actions, genesis_hash, + 0, ); init_txs.push(init_tx); } diff --git a/integration-tests/src/tests/client/sandbox.rs b/integration-tests/src/tests/client/sandbox.rs index 20c960065b1..2545e1f3b8b 100644 --- a/integration-tests/src/tests/client/sandbox.rs +++ b/integration-tests/src/tests/client/sandbox.rs @@ -69,7 +69,8 @@ fn send_tx( actions: Vec, ) -> ProcessTxResponse { let hash = env.clients[0].chain.head().unwrap().last_block_hash; - let tx = SignedTransaction::from_actions(nonce, signer_id, receiver_id, signer, actions, hash); + let tx = + SignedTransaction::from_actions(nonce, signer_id, receiver_id, signer, actions, hash, 0); env.clients[0].process_tx(tx, false, false) } diff --git a/integration-tests/src/tests/nearcore/rpc_nodes.rs b/integration-tests/src/tests/nearcore/rpc_nodes.rs index 5f95324a68f..4a9063d055d 100644 --- a/integration-tests/src/tests/nearcore/rpc_nodes.rs +++ b/integration-tests/src/tests/nearcore/rpc_nodes.rs @@ -453,7 +453,7 @@ fn test_check_unknown_tx_must_return_error() { .EXPERIMENTAL_tx_status(RpcTransactionStatusRequest { transaction_info: TransactionInfo::TransactionId { tx_hash, - sender_account_id: transaction.transaction.signer_id, + sender_account_id: transaction.transaction.signer_id().clone(), }, wait_until: TxExecutionStatus::None, }) diff --git a/integration-tests/src/tests/runtime/deployment.rs b/integration-tests/src/tests/runtime/deployment.rs index 850e869c601..1d036325063 100644 --- a/integration-tests/src/tests/runtime/deployment.rs +++ b/integration-tests/src/tests/runtime/deployment.rs @@ -30,6 +30,7 @@ fn test_deploy_max_size_contract() { &*node_user.signer(), vec![Action::DeployContract(DeployContractAction { code: vec![0u8] })], block_hash, + 0, ); let tx_overhead = signed_transaction.get_size(); diff --git a/integration-tests/src/tests/standard_cases/mod.rs b/integration-tests/src/tests/standard_cases/mod.rs index a61a45bcac6..4f66a885e89 100644 --- a/integration-tests/src/tests/standard_cases/mod.rs +++ b/integration-tests/src/tests/standard_cases/mod.rs @@ -27,7 +27,7 @@ use std::sync::Arc; use crate::node::Node; use crate::user::User; use near_parameters::RuntimeConfig; -use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; +use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum, ReceiptV0}; use near_primitives::test_utils; use near_primitives::transaction::{Action, DeployContractAction, FunctionCallAction}; use testlib::fees_utils::FeeHelper; @@ -1441,12 +1441,12 @@ fn make_receipt(node: &impl Node, actions: Vec, receiver_id: AccountId) input_data_ids: vec![], actions, }); - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: alice_account(), receiver_id, receipt_id: CryptoHash::hash_borsh(&receipt_enum), receipt: receipt_enum, - } + }) } /// Check that numbers of charged trie node accesses during execution of the given receipts matches the provided @@ -1462,7 +1462,7 @@ fn check_trie_nodes_count( let node_user = node.user(); let mut node_touches: Vec<_> = vec![]; let receipt_hashes: Vec = - receipts.iter().map(|receipt| receipt.receipt_id).collect(); + receipts.iter().map(|receipt| *receipt.receipt_id()).collect(); for i in 0..2 { node_user.add_receipts(receipts.clone(), use_flat_storage).unwrap(); diff --git a/integration-tests/src/tests/test_errors.rs b/integration-tests/src/tests/test_errors.rs index 991ce7f2238..9fbd1d67f8a 100644 --- a/integration-tests/src/tests/test_errors.rs +++ b/integration-tests/src/tests/test_errors.rs @@ -49,6 +49,7 @@ fn test_check_tx_error_log() { })), ], block_hash, + 0, ); let tx_result = node.user().commit_transaction(tx).unwrap_err(); @@ -89,6 +90,7 @@ fn test_deliver_tx_error_log() { })), ], block_hash, + 0, ); let tx_result = node.user().commit_transaction(tx).unwrap_err(); diff --git a/integration-tests/src/user/mod.rs b/integration-tests/src/user/mod.rs index 0799bf4570a..e489aa4887d 100644 --- a/integration-tests/src/user/mod.rs +++ b/integration-tests/src/user/mod.rs @@ -106,6 +106,7 @@ pub trait User { &*self.signer(), actions, block_hash, + 0, ); self.commit_transaction(signed_transaction) } diff --git a/integration-tests/src/user/runtime_user.rs b/integration-tests/src/user/runtime_user.rs index eb74e532898..5069f394692 100644 --- a/integration-tests/src/user/runtime_user.rs +++ b/integration-tests/src/user/runtime_user.rs @@ -145,7 +145,7 @@ impl RuntimeUser { return Ok(()); } for receipt in apply_result.outgoing_receipts.iter() { - self.receipts.borrow_mut().insert(receipt.receipt_id, receipt.clone()); + self.receipts.borrow_mut().insert(*receipt.receipt_id(), receipt.clone()); } receipts = apply_result.outgoing_receipts; txs = vec![]; diff --git a/nearcore/src/metrics.rs b/nearcore/src/metrics.rs index 4eaab934c73..cf7b67c15b9 100644 --- a/nearcore/src/metrics.rs +++ b/nearcore/src/metrics.rs @@ -98,8 +98,8 @@ fn log_trie_item(key: Vec, value: Vec) { tracing::trace!( target: "metrics", "trie-stats - PostponedReceipt(predecessor_id: {:?}, receiver_id: {:?})", - receipt.predecessor_id, - receipt.receiver_id, + receipt.predecessor_id(), + receipt.receiver_id(), ); } _ => { diff --git a/runtime/runtime-params-estimator/src/action_costs.rs b/runtime/runtime-params-estimator/src/action_costs.rs index 8b0f2951a12..8184e9059a2 100644 --- a/runtime/runtime-params-estimator/src/action_costs.rs +++ b/runtime/runtime-params-estimator/src/action_costs.rs @@ -13,7 +13,7 @@ use crate::utils::{average_cost, percentiles}; use near_crypto::{KeyType, PublicKey}; use near_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; use near_primitives::hash::CryptoHash; -use near_primitives::receipt::{ActionReceipt, Receipt}; +use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptV0}; use near_primitives::transaction::Action; use near_primitives::types::{AccountId, Gas}; use std::iter; @@ -264,12 +264,12 @@ impl ActionEstimation { input_data_ids: vec![], actions, }; - let receipt = Receipt { + let receipt = Receipt::V0(ReceiptV0 { predecessor_id, receiver_id, receipt_id: CryptoHash::new(), receipt: near_primitives::receipt::ReceiptEnum::Action(action_receipt), - }; + }); testbed.apply_action_receipt(&receipt, self.metric) } diff --git a/runtime/runtime-params-estimator/src/transaction_builder.rs b/runtime/runtime-params-estimator/src/transaction_builder.rs index d19040f3936..412fea0d5cd 100644 --- a/runtime/runtime-params-estimator/src/transaction_builder.rs +++ b/runtime/runtime-params-estimator/src/transaction_builder.rs @@ -62,6 +62,7 @@ impl TransactionBuilder { &signer, actions, CryptoHash::default(), + 0, ) } diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 34a3d0fbafc..4a595bb302e 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -14,7 +14,9 @@ use near_primitives::checked_feature; use near_primitives::config::ViewConfig; use near_primitives::errors::{ActionError, ActionErrorKind, InvalidAccessKeyError, RuntimeError}; use near_primitives::hash::CryptoHash; -use near_primitives::receipt::{ActionReceipt, DataReceipt, Receipt, ReceiptEnum}; +use near_primitives::receipt::{ + ActionReceipt, DataReceipt, Receipt, ReceiptEnum, ReceiptPriority, ReceiptV0, +}; use near_primitives::transaction::{ Action, AddKeyAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction, FunctionCallAction, StakeAction, @@ -268,7 +270,7 @@ pub(crate) fn action_function_call( apply_state, &mut runtime_ext, account, - &receipt.predecessor_id, + receipt.predecessor_id(), action_receipt, promise_results, function_call, @@ -363,7 +365,7 @@ pub(crate) fn action_function_call( actions: receipt.actions, }; - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: account_id.clone(), receiver_id: receipt.receiver_id, // Actual receipt ID is set in the Runtime.apply_action_receipt(...) in the @@ -374,7 +376,7 @@ pub(crate) fn action_function_call( } else { ReceiptEnum::Action(new_action_receipt) }, - } + }) }) .collect(); @@ -382,7 +384,7 @@ pub(crate) fn action_function_call( new_receipts.extend(receipt_manager.data_receipts.into_iter().map(|receipt| { let new_data_receipt = DataReceipt { data_id: receipt.data_id, data: receipt.data }; - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: account_id.clone(), receiver_id: account_id.clone(), // Actual receipt ID is set in the Runtime.apply_action_receipt(...) in the @@ -393,7 +395,7 @@ pub(crate) fn action_function_call( } else { ReceiptEnum::Data(new_data_receipt) }, - } + }) })); // Commit metadata for yielded promises queue @@ -726,12 +728,14 @@ pub(crate) fn action_delete_account( // We use current amount as a pay out to beneficiary. let account_balance = account.as_ref().unwrap().amount(); if account_balance > 0 { - result - .new_receipts - .push(Receipt::new_balance_refund(&delete_account.beneficiary_id, account_balance)); + result.new_receipts.push(Receipt::new_balance_refund( + &delete_account.beneficiary_id, + account_balance, + ReceiptPriority::NoPriority, + )); } remove_account(state_update, account_id)?; - *actor_id = receipt.predecessor_id.clone(); + *actor_id = receipt.predecessor_id().clone(); *account = None; Ok(()) } @@ -826,6 +830,7 @@ pub(crate) fn apply_delegate_action( sender_id: &AccountId, signed_delegate_action: &SignedDelegateAction, result: &mut ActionResult, + _priority: ReceiptPriority, ) -> Result<(), RuntimeError> { let delegate_action = &signed_delegate_action.delegate_action; @@ -854,7 +859,7 @@ pub(crate) fn apply_delegate_action( } // Generate a new receipt from DelegateAction. - let new_receipt = Receipt { + let new_receipt = Receipt::V0(ReceiptV0 { predecessor_id: sender_id.clone(), receiver_id: delegate_action.receiver_id.clone(), receipt_id: CryptoHash::default(), @@ -867,7 +872,7 @@ pub(crate) fn apply_delegate_action( input_data_ids: vec![], actions: delegate_action.get_actions(), }), - }; + }); // Note, Relayer prepaid all fees and all things required by actions: attached deposits and attached gas. // If something goes wrong, deposit is refunded to the predecessor, this is sender_id/Sender in DelegateAction. @@ -892,13 +897,13 @@ pub(crate) fn apply_delegate_action( /// Returns Gas amount is required to execute Receipt and all actions it contains fn receipt_required_gas(apply_state: &ApplyState, receipt: &Receipt) -> Result { - Ok(match &receipt.receipt { + Ok(match receipt.receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { let mut required_gas = safe_add_gas( total_prepaid_exec_fees( &apply_state.config, &action_receipt.actions, - &receipt.receiver_id, + receipt.receiver_id(), )?, total_prepaid_gas(&action_receipt.actions)?, )?; @@ -1310,7 +1315,11 @@ mod tests { Some(Account::new(100, 0, 0, *code_hash, storage_usage, PROTOCOL_VERSION)); let mut actor_id = account_id.clone(); let mut action_result = ActionResult::default(); - let receipt = Receipt::new_balance_refund(&"alice.near".parse().unwrap(), 0); + let receipt = Receipt::new_balance_refund( + &"alice.near".parse().unwrap(), + 0, + ReceiptPriority::NoPriority, + ); let res = action_delete_account( state_update, &mut account, @@ -1484,13 +1493,14 @@ mod tests { &sender_id, &signed_delegate_action, &mut result, + ReceiptPriority::NoPriority, ) .expect("Expect ok"); assert!(result.result.is_ok(), "Result error: {:?}", result.result.err()); assert_eq!( result.new_receipts, - vec![Receipt { + vec![Receipt::V0(ReceiptV0 { predecessor_id: sender_id.clone(), receiver_id: signed_delegate_action.delegate_action.receiver_id.clone(), receipt_id: CryptoHash::default(), @@ -1501,8 +1511,8 @@ mod tests { output_data_receivers: Vec::new(), input_data_ids: Vec::new(), actions: signed_delegate_action.delegate_action.get_actions(), - }) - }] + }), + })] ); } @@ -1528,6 +1538,7 @@ mod tests { &sender_id, &signed_delegate_action, &mut result, + ReceiptPriority::NoPriority, ) .expect("Expect ok"); @@ -1554,6 +1565,7 @@ mod tests { &sender_id, &signed_delegate_action, &mut result, + ReceiptPriority::NoPriority, ) .expect("Expect ok"); @@ -1580,6 +1592,7 @@ mod tests { &"www.test.near".parse().unwrap(), &signed_delegate_action, &mut result, + ReceiptPriority::NoPriority, ) .expect("Expect ok"); diff --git a/runtime/runtime/src/balance_checker.rs b/runtime/runtime/src/balance_checker.rs index 27cfc8c7add..5fcdb293cd6 100644 --- a/runtime/runtime/src/balance_checker.rs +++ b/runtime/runtime/src/balance_checker.rs @@ -39,13 +39,17 @@ fn receipt_cost( config: &RuntimeConfig, receipt: &Receipt, ) -> Result { - Ok(match &receipt.receipt { + Ok(match receipt.receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { let mut total_cost = total_deposit(&action_receipt.actions)?; - if !receipt.predecessor_id.is_system() { + if !receipt.predecessor_id().is_system() { let mut total_gas = safe_add_gas( config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), - total_prepaid_exec_fees(config, &action_receipt.actions, &receipt.receiver_id)?, + total_prepaid_exec_fees( + config, + &action_receipt.actions, + receipt.receiver_id(), + )?, )?; total_gas = safe_add_gas(total_gas, total_prepaid_gas(&action_receipt.actions)?)?; total_gas = safe_add_gas( @@ -159,10 +163,10 @@ pub(crate) fn check_balance( // Accounts let mut all_accounts_ids: HashSet = transactions .iter() - .map(|tx| tx.transaction.signer_id.clone()) - .chain(incoming_receipts.iter().map(|r| r.receiver_id.clone())) - .chain(yield_timeout_receipts.iter().map(|r| r.receiver_id.clone())) - .chain(processed_delayed_receipts.iter().map(|r| r.receiver_id.clone())) + .map(|tx| tx.transaction.signer_id().clone()) + .chain(incoming_receipts.iter().map(|r| r.receiver_id().clone())) + .chain(yield_timeout_receipts.iter().map(|r| r.receiver_id().clone())) + .chain(processed_delayed_receipts.iter().map(|r| r.receiver_id().clone())) .collect(); let incoming_validator_rewards = if let Some(validator_accounts_update) = validator_accounts_update { @@ -202,11 +206,13 @@ pub(crate) fn check_balance( .chain(processed_delayed_receipts.iter()) .chain(yield_timeout_receipts.iter()) .filter_map(|receipt| { - let account_id = &receipt.receiver_id; - match &receipt.receipt { - ReceiptEnum::Action(_) => { - Some(Ok((PostponedReceiptType::Action, account_id.clone(), receipt.receipt_id))) - } + let account_id = receipt.receiver_id(); + match receipt.receipt() { + ReceiptEnum::Action(_) => Some(Ok(( + PostponedReceiptType::Action, + account_id.clone(), + *receipt.receipt_id(), + ))), ReceiptEnum::Data(data_receipt) => { let result = get( initial_state, @@ -288,7 +294,7 @@ mod tests { use crate::ApplyStats; use near_crypto::{InMemorySigner, KeyType}; use near_primitives::hash::{hash, CryptoHash}; - use near_primitives::receipt::ActionReceipt; + use near_primitives::receipt::{ActionReceipt, ReceiptPriority, ReceiptV0}; use near_primitives::test_utils::account_new; use near_primitives::transaction::{Action, TransferAction}; use near_primitives::types::{MerkleHash, StateChangeCause}; @@ -332,7 +338,7 @@ mod tests { &RuntimeConfig::test(), &final_state, &None, - &[Receipt::new_balance_refund(&alice_account(), 1000)], + &[Receipt::new_balance_refund(&alice_account(), 1000, ReceiptPriority::NoPriority)], &[], &[], &[], @@ -392,7 +398,11 @@ mod tests { &RuntimeConfig::test(), &final_state, &None, - &[Receipt::new_balance_refund(&account_id, refund_balance)], + &[Receipt::new_balance_refund( + &account_id, + refund_balance, + ReceiptPriority::NoPriority, + )], &[], &[], &[], @@ -443,19 +453,19 @@ mod tests { deposit, CryptoHash::default(), ); - let receipt = Receipt { - predecessor_id: tx.transaction.signer_id.clone(), - receiver_id: tx.transaction.receiver_id.clone(), + let receipt = Receipt::V0(ReceiptV0 { + predecessor_id: tx.transaction.signer_id().clone(), + receiver_id: tx.transaction.receiver_id().clone(), receipt_id: Default::default(), receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: tx.transaction.signer_id.clone(), - signer_public_key: tx.transaction.public_key.clone(), + signer_id: tx.transaction.signer_id().clone(), + signer_public_key: tx.transaction.public_key().clone(), gas_price, output_data_receivers: vec![], input_data_ids: vec![], actions: vec![Action::Transfer(TransferAction { deposit })], }), - }; + }); check_balance( &cfg, @@ -502,19 +512,19 @@ mod tests { let tx = SignedTransaction::send_money(0, alice_id, bob_id, &signer, 2, CryptoHash::default()); - let receipt = Receipt { - predecessor_id: tx.transaction.signer_id.clone(), - receiver_id: tx.transaction.receiver_id.clone(), + let receipt = Receipt::V0(ReceiptV0 { + predecessor_id: tx.transaction.signer_id().clone(), + receiver_id: tx.transaction.receiver_id().clone(), receipt_id: Default::default(), receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: tx.transaction.signer_id.clone(), - signer_public_key: tx.transaction.public_key.clone(), + signer_id: tx.transaction.signer_id().clone(), + signer_public_key: tx.transaction.public_key().clone(), gas_price, output_data_receivers: vec![], input_data_ids: vec![], actions: vec![Action::Transfer(TransferAction { deposit })], }), - }; + }); assert_eq!( check_balance( @@ -557,19 +567,19 @@ mod tests { let tx = SignedTransaction::send_money(0, alice_id, bob_id, &signer, 1, CryptoHash::default()); - let receipt = Receipt { - predecessor_id: tx.transaction.signer_id.clone(), - receiver_id: tx.transaction.receiver_id.clone(), + let receipt = Receipt::V0(ReceiptV0 { + predecessor_id: tx.transaction.signer_id().clone(), + receiver_id: tx.transaction.receiver_id().clone(), receipt_id: Default::default(), receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: tx.transaction.signer_id.clone(), - signer_public_key: tx.transaction.public_key.clone(), + signer_id: tx.transaction.signer_id().clone(), + signer_public_key: tx.transaction.public_key().clone(), gas_price, output_data_receivers: vec![], input_data_ids: vec![], actions: vec![Action::Transfer(TransferAction { deposit })], }), - }; + }); // Alice's balance becomes u128::MAX, which causes it is interpreted as // the Alice's account version to be 2 or higher, instead of being interpreted diff --git a/runtime/runtime/src/config.rs b/runtime/runtime/src/config.rs index c5681c8481f..a833af19697 100644 --- a/runtime/runtime/src/config.rs +++ b/runtime/runtime/src/config.rs @@ -259,17 +259,18 @@ pub fn tx_cost( total_send_fees( config, sender_is_receiver, - &transaction.actions, - &transaction.receiver_id, + transaction.actions(), + transaction.receiver_id(), )?, )?; let prepaid_gas = safe_add_gas( - total_prepaid_gas(&transaction.actions)?, - total_prepaid_send_fees(config, &transaction.actions)?, + total_prepaid_gas(&transaction.actions())?, + total_prepaid_send_fees(config, &transaction.actions())?, )?; // If signer is equals to receiver the receipt will be processed at the same block as this // transaction. Otherwise it will processed in the next block and the gas might be inflated. - let initial_receipt_hop = if transaction.signer_id == transaction.receiver_id { 0 } else { 1 }; + let initial_receipt_hop = + if transaction.signer_id() == transaction.receiver_id() { 0 } else { 1 }; let minimum_new_receipt_gas = if protocol_version < FIXED_MINIMUM_NEW_RECEIPT_GAS_VERSION { fees.min_receipt_with_function_call_gas() } else { @@ -298,12 +299,12 @@ pub fn tx_cost( safe_add_gas(prepaid_gas, fees.fee(ActionCosts::new_action_receipt).exec_fee())?; gas_remaining = safe_add_gas( gas_remaining, - total_prepaid_exec_fees(config, &transaction.actions, &transaction.receiver_id)?, + total_prepaid_exec_fees(config, transaction.actions(), transaction.receiver_id())?, )?; let burnt_amount = safe_gas_to_balance(gas_price, gas_burnt)?; let remaining_gas_amount = safe_gas_to_balance(receipt_gas_price, gas_remaining)?; let mut total_cost = safe_add_balance(burnt_amount, remaining_gas_amount)?; - total_cost = safe_add_balance(total_cost, total_deposit(&transaction.actions)?)?; + total_cost = safe_add_balance(total_cost, total_deposit(&transaction.actions())?)?; Ok(TransactionCost { gas_burnt, gas_remaining, receipt_gas_price, total_cost, burnt_amount }) } diff --git a/runtime/runtime/src/congestion_control.rs b/runtime/runtime/src/congestion_control.rs index 8afb3ae11ac..ca648dabe57 100644 --- a/runtime/runtime/src/congestion_control.rs +++ b/runtime/runtime/src/congestion_control.rs @@ -186,7 +186,7 @@ impl ReceiptSinkV2<'_> { epoch_info_provider: &dyn EpochInfoProvider, ) -> Result<(), RuntimeError> { let shard = epoch_info_provider - .account_id_to_shard_id(&receipt.receiver_id, &apply_state.epoch_id)?; + .account_id_to_shard_id(receipt.receiver_id(), &apply_state.epoch_id)?; if shard == apply_state.shard_id { // No limits on receipts that stay on the same shard. Backpressure // wouldn't help, the receipt takes the same memory if buffered or @@ -260,11 +260,11 @@ fn receipt_congestion_gas( receipt: &Receipt, config: &RuntimeConfig, ) -> Result { - match &receipt.receipt { + match receipt.receipt() { ReceiptEnum::Action(action_receipt) => { // account for gas guaranteed to be used for executing the receipts let prepaid_exec_gas = safe_add_gas( - total_prepaid_exec_fees(config, &action_receipt.actions, &receipt.receiver_id)?, + total_prepaid_exec_fees(config, &action_receipt.actions, receipt.receiver_id())?, config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), )?; // account for gas guaranteed to be used for creating new receipts diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 1a9fbae959a..118e1147308 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -25,7 +25,7 @@ use near_primitives::errors::{ use near_primitives::hash::CryptoHash; use near_primitives::receipt::{ ActionReceipt, DataReceipt, DelayedReceiptIndices, PromiseYieldIndices, PromiseYieldTimeout, - Receipt, ReceiptEnum, ReceivedData, + Receipt, ReceiptEnum, ReceiptV0, ReceivedData, }; use near_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; use near_primitives::sandbox::state_patch::SandboxStatePatch; @@ -307,19 +307,19 @@ impl Runtime { &apply_state.prev_block_hash, &apply_state.block_hash, ); - let receipt = Receipt { - predecessor_id: transaction.signer_id.clone(), - receiver_id: transaction.receiver_id.clone(), + let receipt = Receipt::V0(ReceiptV0 { + predecessor_id: transaction.signer_id().clone(), + receiver_id: transaction.receiver_id().clone(), receipt_id, receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: transaction.signer_id.clone(), - signer_public_key: transaction.public_key.clone(), + signer_id: transaction.signer_id().clone(), + signer_public_key: transaction.public_key().clone(), gas_price: verification_result.receipt_gas_price, output_data_receivers: vec![], input_data_ids: vec![], - actions: transaction.actions.clone(), + actions: transaction.actions().to_vec(), }), - }; + }); stats.tx_burnt_amount = safe_add_balance(stats.tx_burnt_amount, verification_result.burnt_amount)?; let gas_burnt = verification_result.gas_burnt; @@ -327,14 +327,14 @@ impl Runtime { let outcome = ExecutionOutcomeWithId { id: signed_transaction.get_hash(), outcome: ExecutionOutcome { - status: ExecutionStatus::SuccessReceiptId(receipt.receipt_id), + status: ExecutionStatus::SuccessReceiptId(*receipt.receipt_id()), logs: vec![], - receipt_ids: vec![receipt.receipt_id], + receipt_ids: vec![*receipt.receipt_id()], gas_burnt, // TODO(#8806): Support compute costs for actions. For now they match burnt gas. compute_usage: Some(compute_usage), tokens_burnt: verification_result.burnt_amount, - executor_id: transaction.signer_id.clone(), + executor_id: transaction.signer_id().clone(), // TODO: profile data is only counted in apply_action, which only happened at process_receipt // VerificationResult needs updates to incorporate profile data to support profile data of txns metadata: ExecutionMetadata::V1, @@ -367,14 +367,14 @@ impl Runtime { actions: &[Action], epoch_info_provider: &dyn EpochInfoProvider, ) -> Result { - let exec_fees = exec_fee(&apply_state.config, action, &receipt.receiver_id); + let exec_fees = exec_fee(&apply_state.config, action, receipt.receiver_id()); let mut result = ActionResult::default(); result.gas_used = exec_fees; result.gas_burnt = exec_fees; // TODO(#8806): Support compute costs for actions. For now they match burnt gas. result.compute_usage = exec_fees; - let account_id = &receipt.receiver_id; - let is_refund = receipt.predecessor_id.is_system(); + let account_id = receipt.receiver_id(); + let is_refund = receipt.predecessor_id().is_system(); let is_the_only_action = actions.len() == 1; let implicit_account_creation_eligible = is_the_only_action && !is_refund; @@ -405,8 +405,8 @@ impl Runtime { &apply_state.config.account_creation_config, account, actor_id, - &receipt.receiver_id, - &receipt.predecessor_id, + receipt.receiver_id(), + receipt.predecessor_id(), &mut result, apply_state.current_protocol_version, ); @@ -519,6 +519,7 @@ impl Runtime { account_id, signed_delegate_action, &mut result, + receipt.priority(), )?; } }; @@ -536,13 +537,13 @@ impl Runtime { stats: &mut ApplyStats, epoch_info_provider: &dyn EpochInfoProvider, ) -> Result { - let action_receipt = match &receipt.receipt { + let action_receipt = match receipt.receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { action_receipt } _ => unreachable!("given receipt should be an action receipt"), }; - let account_id = &receipt.receiver_id; + let account_id = receipt.receiver_id(); // Collecting input data and removing it from the state let promise_results = action_receipt .input_data_ids @@ -572,7 +573,7 @@ impl Runtime { }); let mut account = get_account(state_update, account_id)?; - let mut actor_id = receipt.predecessor_id.clone(); + let mut actor_id = receipt.predecessor_id().clone(); let mut result = ActionResult::default(); let exec_fees = apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee(); result.gas_used = exec_fees; @@ -586,7 +587,7 @@ impl Runtime { for (action_index, action) in action_receipt.actions.iter().enumerate() { let action_hash = create_action_hash_from_receipt_id( apply_state.current_protocol_version, - &receipt.receipt_id, + receipt.receipt_id(), &apply_state.prev_block_hash, &apply_state.block_hash, action_index, @@ -664,7 +665,7 @@ impl Runtime { } } - let gas_deficit_amount = if receipt.predecessor_id.is_system() { + let gas_deficit_amount = if receipt.predecessor_id().is_system() { // We will set gas_burnt for refund receipts to be 0 when we calculate tx_burnt_amount // Here we don't set result.gas_burnt to be zero if CountRefundReceiptsInGasLimit is // enabled because we want it to be counted in gas limit calculation later @@ -720,7 +721,8 @@ impl Runtime { } // If the receipt is a refund, then we consider it free without burnt gas. - let gas_burnt: Gas = if receipt.predecessor_id.is_system() { 0 } else { result.gas_burnt }; + let gas_burnt: Gas = + if receipt.predecessor_id().is_system() { 0 } else { result.gas_burnt }; // `gas_deficit_amount` is strictly less than `gas_price * gas_burnt`. let mut tx_burnt_amount = safe_gas_to_balance(apply_state.gas_price, gas_burnt)? - gas_deficit_amount; @@ -766,7 +768,7 @@ impl Runtime { .new_receipts .get_mut(receipt_index as usize) .expect("the receipt for the given receipt index should exist") - .receipt + .receipt_mut() { ReceiptEnum::Action(ref mut new_action_receipt) | ReceiptEnum::PromiseYield(ref mut new_action_receipt) => new_action_receipt @@ -781,14 +783,16 @@ impl Runtime { Err(_) => None, }; result.new_receipts.extend(action_receipt.output_data_receivers.iter().map( - |data_receiver| Receipt { - predecessor_id: account_id.clone(), - receiver_id: data_receiver.receiver_id.clone(), - receipt_id: CryptoHash::default(), - receipt: ReceiptEnum::Data(DataReceipt { - data_id: data_receiver.data_id, - data: data.clone(), - }), + |data_receiver| { + Receipt::V0(ReceiptV0 { + predecessor_id: account_id.clone(), + receiver_id: data_receiver.receiver_id.clone(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Data(DataReceipt { + data_id: data_receiver.data_id, + data: data.clone(), + }), + }) }, )); }; @@ -802,15 +806,15 @@ impl Runtime { .filter_map(|(receipt_index, mut new_receipt)| { let receipt_id = create_receipt_id_from_receipt_id( apply_state.current_protocol_version, - &receipt.receipt_id, + receipt.receipt_id(), &apply_state.prev_block_hash, &apply_state.block_hash, receipt_index, ); - new_receipt.receipt_id = receipt_id; + new_receipt.set_receipt_id(receipt_id); let is_action = matches!( - &new_receipt.receipt, + new_receipt.receipt(), ReceiptEnum::Action(_) | ReceiptEnum::PromiseYield(_) ); @@ -834,7 +838,7 @@ impl Runtime { Ok(ReturnData::ReceiptIndex(receipt_index)) => { ExecutionStatus::SuccessReceiptId(create_receipt_id_from_receipt_id( apply_state.current_protocol_version, - &receipt.receipt_id, + receipt.receipt_id(), &apply_state.prev_block_hash, &apply_state.block_hash, receipt_index as usize, @@ -848,7 +852,7 @@ impl Runtime { Self::print_log(&result.logs); Ok(ExecutionOutcomeWithId { - id: receipt.receipt_id, + id: *receipt.receipt_id(), outcome: ExecutionOutcome { status, logs: result.logs, @@ -876,7 +880,7 @@ impl Runtime { total_prepaid_send_fees(config, &action_receipt.actions)?, )?; let prepaid_exec_gas = safe_add_gas( - total_prepaid_exec_fees(config, &action_receipt.actions, &receipt.receiver_id)?, + total_prepaid_exec_fees(config, &action_receipt.actions, receipt.receiver_id())?, config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), )?; let deposit_refund = if result.result.is_err() { total_deposit } else { 0 }; @@ -916,9 +920,11 @@ impl Runtime { } if deposit_refund > 0 { - result - .new_receipts - .push(Receipt::new_balance_refund(&receipt.predecessor_id, deposit_refund)); + result.new_receipts.push(Receipt::new_balance_refund( + receipt.predecessor_id(), + deposit_refund, + receipt.priority(), + )); } if gas_balance_refund > 0 { // Gas refunds refund the allowance of the access key, so if the key exists on the @@ -927,6 +933,7 @@ impl Runtime { &action_receipt.signer_id, gas_balance_refund, action_receipt.signer_public_key.clone(), + receipt.priority(), )); } Ok(gas_deficit_amount) @@ -942,8 +949,8 @@ impl Runtime { stats: &mut ApplyStats, epoch_info_provider: &dyn EpochInfoProvider, ) -> Result, RuntimeError> { - let account_id = &receipt.receiver_id; - match receipt.receipt { + let account_id = receipt.receiver_id(); + match receipt.receipt() { ReceiptEnum::Data(ref data_receipt) => { // Received a new data receipt. // Saving the data into the state keyed by the data_id. @@ -1047,7 +1054,7 @@ impl Runtime { receiver_id: account_id.clone(), data_id: *data_id, }, - &receipt.receipt_id, + receipt.receipt_id(), ) } } @@ -1072,7 +1079,7 @@ impl Runtime { state_update, TrieKey::PendingDataCount { receiver_id: account_id.clone(), - receipt_id: receipt.receipt_id, + receipt_id: *receipt.receipt_id(), }, &pending_data_count, ); @@ -1425,7 +1432,7 @@ impl Runtime { signed_transaction, &mut stats, )?; - if receipt.receiver_id == signed_transaction.transaction.signer_id { + if receipt.receiver_id() == signed_transaction.transaction.signer_id() { local_receipts.push(receipt); } else { receipt_sink.forward_or_buffer_receipt( @@ -1458,10 +1465,10 @@ impl Runtime { let span = tracing::debug_span!( target: "runtime", "process_receipt", - receipt_id = %receipt.receipt_id, - predecessor = %receipt.predecessor_id, - receiver = %receipt.receiver_id, - id = %receipt.receipt_id, + receipt_id = %receipt.receipt_id(), + predecessor = %receipt.predecessor_id(), + receiver = %receipt.receiver_id(), + id = %receipt.receipt_id(), gas_burnt = tracing::field::Empty, compute_usage = tracing::field::Empty, ) @@ -1680,7 +1687,7 @@ impl Runtime { new_receipt_index += 1; // Create a PromiseResume receipt to resolve the timed-out yield. - let resume_receipt = Receipt { + let resume_receipt = Receipt::V0(ReceiptV0 { predecessor_id: queue_entry.account_id.clone(), receiver_id: queue_entry.account_id.clone(), receipt_id: new_receipt_id, @@ -1688,7 +1695,7 @@ impl Runtime { data_id: queue_entry.data_id, data: None, }), - }; + }); // The receipt is destined for the local shard and will be placed in the outgoing // receipts buffer. It is possible that there is already an outgoing receipt resolving @@ -1878,10 +1885,10 @@ fn action_transfer_or_implicit_account_creation( action_transfer(account, deposit)?; } // Check if this is a gas refund, then try to refund the access key allowance. - if is_refund && action_receipt.signer_id == receipt.receiver_id { + if is_refund && &action_receipt.signer_id == receipt.receiver_id() { try_refund_allowance( state_update, - &receipt.receiver_id, + receipt.receiver_id(), &action_receipt.signer_public_key, deposit, )?; @@ -1896,7 +1903,7 @@ fn action_transfer_or_implicit_account_creation( &apply_state.config.fees, account, actor_id, - &receipt.receiver_id, + receipt.receiver_id(), deposit, apply_state.block_height, apply_state.current_protocol_version, @@ -1934,6 +1941,7 @@ mod tests { use near_parameters::{ExtCosts, ParameterCost, RuntimeConfig}; use near_primitives::account::AccessKey; use near_primitives::hash::hash; + use near_primitives::receipt::ReceiptPriority; use near_primitives::shard_layout::ShardUId; use near_primitives::test_utils::{account_new, MockEpochInfoProvider}; use near_primitives::transaction::{ @@ -1959,7 +1967,7 @@ mod tests { signer: Arc, actions: Vec, ) -> Receipt { - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: account_id.clone(), receiver_id: account_id.clone(), receipt_id: CryptoHash::hash_borsh(actions.clone()), @@ -1971,7 +1979,7 @@ mod tests { input_data_ids: vec![], actions, }), - } + }) } #[test] @@ -2106,7 +2114,11 @@ mod tests { tries.get_trie_for_shard(ShardUId::single_shard(), root), &Some(validator_accounts_update), &apply_state, - &[Receipt::new_balance_refund(&alice_account(), small_refund)], + &[Receipt::new_balance_refund( + &alice_account(), + small_refund, + ReceiptPriority::NoPriority, + )], &[], &epoch_info_provider, Default::default(), @@ -2329,7 +2341,7 @@ mod tests { (0..n) .map(|i| { receipt_id = hash(receipt_id.as_ref()); - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: bob_account(), receiver_id: alice_account(), receipt_id, @@ -2343,7 +2355,7 @@ mod tests { deposit: small_transfer + Balance::from(i), })], }), - } + }) }) .collect() } @@ -2353,7 +2365,11 @@ mod tests { (0..n) .map(|i| { receipt_id = hash(receipt_id.as_ref()); - Receipt::new_balance_refund(&alice_account(), small_transfer + Balance::from(i)) + Receipt::new_balance_refund( + &alice_account(), + small_transfer + Balance::from(i), + ReceiptPriority::NoPriority, + ) }) .collect() } @@ -2482,7 +2498,7 @@ mod tests { &apply_state.prev_block_hash, &apply_state.block_hash, ), // receipt for tx 3 - receipts[0].receipt_id, // receipt #0 + *receipts[0].receipt_id(), // receipt #0 ], "STEP #2 failed", ); @@ -2565,8 +2581,8 @@ mod tests { assert_eq!( apply_result.outcomes.iter().map(|o| o.id).collect::>(), vec![ - receipts[1].receipt_id, // receipt #1 - receipts[2].receipt_id, // receipt #2 + *receipts[1].receipt_id(), // receipt #1 + *receipts[2].receipt_id(), // receipt #2 create_receipt_id_from_transaction( PROTOCOL_VERSION, &local_transactions[8], @@ -2595,9 +2611,9 @@ mod tests { assert_eq!( apply_result.outcomes.iter().map(|o| o.id).collect::>(), vec![ - receipts[3].receipt_id, // receipt #3 - receipts[4].receipt_id, // receipt #4 - receipts[5].receipt_id, // receipt #5 + *receipts[3].receipt_id(), // receipt #3 + *receipts[4].receipt_id(), // receipt #4 + *receipts[5].receipt_id(), // receipt #5 ], "STEP #5 failed", ); @@ -2614,7 +2630,7 @@ mod tests { let n = 1; let mut receipts = generate_receipts(small_transfer, n); - if let ReceiptEnum::Action(action_receipt) = &mut receipts.get_mut(0).unwrap().receipt { + if let ReceiptEnum::Action(action_receipt) = receipts.get_mut(0).unwrap().receipt_mut() { action_receipt.gas_price = GAS_PRICE / 10; } @@ -2654,7 +2670,7 @@ mod tests { total_prepaid_exec_fees(&apply_state.config, &actions, &alice_account()).unwrap(), ) .unwrap(); - let receipts = vec![Receipt { + let receipts = vec![Receipt::V0(ReceiptV0 { predecessor_id: bob_account(), receiver_id: alice_account(), receipt_id: CryptoHash::default(), @@ -2666,7 +2682,7 @@ mod tests { input_data_ids: vec![], actions, }), - }]; + })]; let total_receipt_cost = Balance::from(gas + expected_gas_burnt) * gas_price; let expected_gas_burnt_amount = Balance::from(expected_gas_burnt) * GAS_PRICE; let expected_refund = total_receipt_cost - expected_gas_burnt_amount; @@ -2685,7 +2701,7 @@ mod tests { // We used part of the prepaid gas to paying extra fees. assert_eq!(result.stats.gas_deficit_amount, 0); // The refund is less than the received amount. - match &result.outgoing_receipts[0].receipt { + match result.outgoing_receipts[0].receipt() { ReceiptEnum::Action(ActionReceipt { actions, .. }) => { assert!( matches!(actions[0], Action::Transfer(TransferAction { deposit }) if deposit == expected_refund) @@ -2717,7 +2733,7 @@ mod tests { total_prepaid_exec_fees(&apply_state.config, &actions, &alice_account()).unwrap(), ) .unwrap(); - let receipts = vec![Receipt { + let receipts = vec![Receipt::V0(ReceiptV0 { predecessor_id: bob_account(), receiver_id: alice_account(), receipt_id: CryptoHash::default(), @@ -2729,7 +2745,7 @@ mod tests { input_data_ids: vec![], actions, }), - }]; + })]; let total_receipt_cost = Balance::from(gas + expected_gas_burnt) * gas_price; let expected_gas_burnt_amount = Balance::from(expected_gas_burnt) * GAS_PRICE; let expected_deficit = expected_gas_burnt_amount - total_receipt_cost; @@ -2956,10 +2972,10 @@ mod tests { // Only first two receipts should fit into the chunk due to the compute usage limit. assert_matches!(&apply_result.outcomes[..], [first, second] => { - assert_eq!(first.id, deploy_contract_receipt.receipt_id); + assert_eq!(first.id, *deploy_contract_receipt.receipt_id()); assert_matches!(first.outcome.status, ExecutionStatus::SuccessValue(_)); - assert_eq!(second.id, first_call_receipt.receipt_id); + assert_eq!(second.id, *first_call_receipt.receipt_id()); assert_eq!(second.outcome.compute_usage.unwrap(), sha256_cost.compute); assert_matches!(second.outcome.status, ExecutionStatus::SuccessValue(_)); }); @@ -2977,7 +2993,7 @@ mod tests { .unwrap(); assert_matches!(&apply_result.outcomes[..], [ExecutionOutcomeWithId { id, outcome }] => { - assert_eq!(*id, second_call_receipt.receipt_id); + assert_eq!(id, second_call_receipt.receipt_id()); assert_eq!(outcome.compute_usage.unwrap(), sha256_cost.compute); assert_matches!(outcome.status, ExecutionStatus::SuccessValue(_)); }); @@ -3020,10 +3036,10 @@ mod tests { .unwrap(); assert_matches!(&apply_result.outcomes[..], [first, second] => { - assert_eq!(first.id, deploy_contract_receipt.receipt_id); + assert_eq!(first.id, *deploy_contract_receipt.receipt_id()); assert_matches!(first.outcome.status, ExecutionStatus::SuccessValue(_)); - assert_eq!(second.id, first_call_receipt.receipt_id); + assert_eq!(second.id, *first_call_receipt.receipt_id()); assert_matches!(second.outcome.status, ExecutionStatus::Failure(_)); }); } diff --git a/runtime/runtime/src/prefetch.rs b/runtime/runtime/src/prefetch.rs index a3cd5c790da..340a7e0d0b8 100644 --- a/runtime/runtime/src/prefetch.rs +++ b/runtime/runtime/src/prefetch.rs @@ -92,8 +92,8 @@ impl TriePrefetcher { receipts: &[Receipt], ) -> Result<(), PrefetchError> { for receipt in receipts.iter() { - let is_refund = receipt.predecessor_id.is_system(); - let action_receipt = match &receipt.receipt { + let is_refund = receipt.predecessor_id().is_system(); + let action_receipt = match receipt.receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { action_receipt } @@ -101,7 +101,7 @@ impl TriePrefetcher { continue; } }; - let account_id = receipt.receiver_id.clone(); + let account_id = receipt.receiver_id().clone(); // general-purpose account prefetching if self.prefetch_api.enable_receipt_prefetching { @@ -154,7 +154,7 @@ impl TriePrefetcher { } if self.prefetch_api.sweat_prefetch_receivers.contains(&account_id) - && self.prefetch_api.sweat_prefetch_senders.contains(&receipt.predecessor_id) + && self.prefetch_api.sweat_prefetch_senders.contains(receipt.predecessor_id()) { if fn_call.method_name == "record_batch" { self.prefetch_sweat_record_batch(account_id.clone(), &fn_call.args)?; @@ -166,7 +166,7 @@ impl TriePrefetcher { let config = claim_sweat_cfg.iter().find(|cfg| { cfg.receiver == account_id.as_str() && cfg.method_name == fn_call.method_name - && cfg.sender == receipt.predecessor_id.as_str() + && cfg.sender == receipt.predecessor_id().as_str() }); if config.is_some() { self.prefetch_claim_sweat_record_batch_for_hold( @@ -183,13 +183,13 @@ impl TriePrefetcher { if config.is_some() { self.prefetch_claim_sweat_claim( account_id.clone(), - receipt.predecessor_id.clone(), + receipt.predecessor_id().clone(), )? } } if self.prefetch_api.kaiching_prefetch_config.iter().any(|cfg| { - cfg.sender == receipt.predecessor_id.as_str() + cfg.sender == receipt.predecessor_id().as_str() && cfg.receiver == account_id.as_str() && cfg.method_name == fn_call.method_name }) { @@ -211,13 +211,13 @@ impl TriePrefetcher { ) -> Result<(), PrefetchError> { if self.prefetch_api.enable_receipt_prefetching { for t in transactions { - let account_id = t.transaction.signer_id.clone(); + let account_id = t.transaction.signer_id().clone(); let trie_key = TrieKey::Account { account_id }; self.prefetch_trie_key(trie_key)?; let trie_key = TrieKey::AccessKey { - account_id: t.transaction.signer_id.clone(), - public_key: t.transaction.public_key.clone(), + account_id: t.transaction.signer_id().clone(), + public_key: t.transaction.public_key().clone(), }; self.prefetch_trie_key(trie_key)?; } diff --git a/runtime/runtime/src/verifier.rs b/runtime/runtime/src/verifier.rs index 2c4647e80e3..14c1c9e4029 100644 --- a/runtime/runtime/src/verifier.rs +++ b/runtime/runtime/src/verifier.rs @@ -98,13 +98,17 @@ pub fn validate_transaction( verify_signature: bool, current_protocol_version: ProtocolVersion, ) -> Result { + // Don't allow V1 currently. This will be changed when the new protocol version is introduced. + if matches!(signed_transaction.transaction, near_primitives::transaction::Transaction::V1(_)) { + return Err(InvalidTxError::InvalidTransactionVersion.into()); + } let transaction = &signed_transaction.transaction; - let signer_id = &transaction.signer_id; + let signer_id = transaction.signer_id(); if verify_signature && !signed_transaction .signature - .verify(signed_transaction.get_hash().as_ref(), &transaction.public_key) + .verify(signed_transaction.get_hash().as_ref(), transaction.public_key()) { return Err(InvalidTxError::InvalidSignature.into()); } @@ -121,12 +125,12 @@ pub fn validate_transaction( validate_actions( &config.wasm_config.limit_config, - &transaction.actions, + transaction.actions(), current_protocol_version, ) .map_err(InvalidTxError::ActionsValidation)?; - let sender_is_receiver = &transaction.receiver_id == signer_id; + let sender_is_receiver = transaction.receiver_id() == signer_id; tx_cost(&config, transaction, gas_price, sender_is_receiver, current_protocol_version) .map_err(|_| InvalidTxError::CostOverflow.into()) @@ -151,8 +155,9 @@ pub fn verify_and_charge_transaction( verify_signature, current_protocol_version, )?; + let transaction = &signed_transaction.transaction; - let signer_id = &transaction.signer_id; + let signer_id = transaction.signer_id(); let mut signer = match get_account(state_update, signer_id)? { Some(signer) => signer, @@ -160,22 +165,22 @@ pub fn verify_and_charge_transaction( return Err(InvalidTxError::SignerDoesNotExist { signer_id: signer_id.clone() }.into()); } }; - let mut access_key = match get_access_key(state_update, signer_id, &transaction.public_key)? { + let mut access_key = match get_access_key(state_update, signer_id, transaction.public_key())? { Some(access_key) => access_key, None => { return Err(InvalidTxError::InvalidAccessKeyError( InvalidAccessKeyError::AccessKeyNotFound { account_id: signer_id.clone(), - public_key: transaction.public_key.clone().into(), + public_key: transaction.public_key().clone().into(), }, ) .into()); } }; - if transaction.nonce <= access_key.nonce { + if transaction.nonce() <= access_key.nonce { return Err(InvalidTxError::InvalidNonce { - tx_nonce: transaction.nonce, + tx_nonce: transaction.nonce(), ak_nonce: access_key.nonce, } .into()); @@ -184,9 +189,9 @@ pub fn verify_and_charge_transaction( if let Some(height) = block_height { let upper_bound = height * near_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; - if transaction.nonce >= upper_bound { + if transaction.nonce() >= upper_bound { return Err(InvalidTxError::NonceTooLarge { - tx_nonce: transaction.nonce, + tx_nonce: transaction.nonce(), upper_bound, } .into()); @@ -194,7 +199,7 @@ pub fn verify_and_charge_transaction( } }; - access_key.nonce = transaction.nonce; + access_key.nonce = transaction.nonce(); signer.set_amount(signer.amount().checked_sub(total_cost).ok_or_else(|| { InvalidTxError::NotEnoughBalance { @@ -211,7 +216,7 @@ pub fn verify_and_charge_transaction( *allowance = allowance.checked_sub(total_cost).ok_or_else(|| { InvalidTxError::InvalidAccessKeyError(InvalidAccessKeyError::NotEnoughAllowance { account_id: signer_id.clone(), - public_key: transaction.public_key.clone().into(), + public_key: transaction.public_key().clone().into(), allowance: *allowance, cost: total_cost, }) @@ -234,23 +239,23 @@ pub fn verify_and_charge_transaction( }; if let AccessKeyPermission::FunctionCall(ref function_call_permission) = access_key.permission { - if transaction.actions.len() != 1 { + if transaction.actions().len() != 1 { return Err(InvalidTxError::InvalidAccessKeyError( InvalidAccessKeyError::RequiresFullAccess, ) .into()); } - if let Some(Action::FunctionCall(ref function_call)) = transaction.actions.get(0) { + if let Some(Action::FunctionCall(ref function_call)) = transaction.actions().get(0) { if function_call.deposit > 0 { return Err(InvalidTxError::InvalidAccessKeyError( InvalidAccessKeyError::DepositWithFunctionCall, ) .into()); } - if transaction.receiver_id != function_call_permission.receiver_id { + if transaction.receiver_id() != &function_call_permission.receiver_id { return Err(InvalidTxError::InvalidAccessKeyError( InvalidAccessKeyError::ReceiverMismatch { - tx_receiver: transaction.receiver_id.clone(), + tx_receiver: transaction.receiver_id().clone(), ak_receiver: function_call_permission.receiver_id.clone(), }, ) @@ -277,7 +282,7 @@ pub fn verify_and_charge_transaction( } }; - set_access_key(state_update, signer_id.clone(), transaction.public_key.clone(), &access_key); + set_access_key(state_update, signer_id.clone(), transaction.public_key().clone(), &access_key); set_account(state_update, signer_id.clone(), &signer); Ok(VerificationResult { gas_burnt, gas_remaining, receipt_gas_price, burnt_amount }) @@ -292,16 +297,16 @@ pub(crate) fn validate_receipt( // We retain these checks here as to maintain backwards compatibility // with AccountId validation since we illegally parse an AccountId // in near-vm-logic/logic.rs#fn(VMLogic::read_and_parse_account_id) - AccountId::validate(receipt.predecessor_id.as_ref()).map_err(|_| { + AccountId::validate(receipt.predecessor_id().as_ref()).map_err(|_| { ReceiptValidationError::InvalidPredecessorId { - account_id: receipt.predecessor_id.to_string(), + account_id: receipt.predecessor_id().to_string(), } })?; - AccountId::validate(receipt.receiver_id.as_ref()).map_err(|_| { - ReceiptValidationError::InvalidReceiverId { account_id: receipt.receiver_id.to_string() } + AccountId::validate(receipt.receiver_id().as_ref()).map_err(|_| { + ReceiptValidationError::InvalidReceiverId { account_id: receipt.receiver_id().to_string() } })?; - match &receipt.receipt { + match receipt.receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { validate_action_receipt(limit_config, action_receipt, current_protocol_version) } @@ -573,6 +578,7 @@ mod tests { use near_primitives::account::{AccessKey, FunctionCallPermission}; use near_primitives::action::delegate::{DelegateAction, NonDelegateAction}; use near_primitives::hash::{hash, CryptoHash}; + use near_primitives::receipt::ReceiptPriority; use near_primitives::test_utils::account_new; use near_primitives::transaction::{ CreateAccountAction, DeleteAccountAction, DeleteKeyAction, StakeAction, TransferAction, @@ -960,6 +966,7 @@ mod tests { deposit: 0, }))], CryptoHash::default(), + 0, ), RuntimeError::InvalidTxError(InvalidTxError::ActionsValidation( ActionsValidationError::TotalPrepaidGasExceeded { @@ -1053,6 +1060,29 @@ mod tests { ); } + #[test] + fn test_validate_transaction_invalid_transaction_version() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + assert_err_both_validations( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions_v1( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::Transfer(TransferAction { deposit: 100 })], + CryptoHash::default(), + 1, + ), + RuntimeError::InvalidTxError(InvalidTxError::InvalidTransactionVersion), + ); + } + #[test] fn test_validate_transaction_invalid_not_enough_balance() { let config = RuntimeConfig::test(); @@ -1122,6 +1152,7 @@ mod tests { deposit: 0, }))], CryptoHash::default(), + 0, ), true, None, @@ -1257,6 +1288,7 @@ mod tests { Action::CreateAccount(CreateAccountAction {}) ], CryptoHash::default(), + 0 ), true, None, @@ -1280,6 +1312,7 @@ mod tests { &*signer, vec![], CryptoHash::default(), + 0 ), true, None, @@ -1303,6 +1336,7 @@ mod tests { &*signer, vec![Action::CreateAccount(CreateAccountAction {})], CryptoHash::default(), + 0 ), true, None, @@ -1348,6 +1382,7 @@ mod tests { deposit: 0, })),], CryptoHash::default(), + 0 ), true, None, @@ -1396,6 +1431,7 @@ mod tests { deposit: 0, })),], CryptoHash::default(), + 0 ), true, None, @@ -1441,6 +1477,7 @@ mod tests { deposit: 100, })),], CryptoHash::default(), + 0 ), true, None, @@ -1465,6 +1502,7 @@ mod tests { &*signer, vec![Action::DeployContract(DeployContractAction { code: vec![1; 5] })], CryptoHash::default(), + 0, ); let transaction_size = transaction.get_size(); @@ -1509,7 +1547,7 @@ mod tests { let limit_config = test_limit_config(); validate_receipt( &limit_config, - &Receipt::new_balance_refund(&alice_account(), 10), + &Receipt::new_balance_refund(&alice_account(), 10, ReceiptPriority::NoPriority), PROTOCOL_VERSION, ) .expect("valid receipt"); diff --git a/runtime/runtime/tests/runtime_group_tools/mod.rs b/runtime/runtime/tests/runtime_group_tools/mod.rs index 03caac12721..e1e5307b2ac 100644 --- a/runtime/runtime/tests/runtime_group_tools/mod.rs +++ b/runtime/runtime/tests/runtime_group_tools/mod.rs @@ -268,7 +268,7 @@ impl RuntimeGroup { .0 .lock() .unwrap() - .get_mut(&transaction.transaction.signer_id) + .get_mut(transaction.transaction.signer_id()) .unwrap() .incoming_transactions .push(transaction); @@ -334,7 +334,8 @@ impl RuntimeGroup { mailbox.incoming_transactions.clear(); group.transaction_logs.lock().unwrap().extend(transaction_results); for new_receipt in new_receipts { - let locked_other_mailbox = mailboxes.get_mut(&new_receipt.receiver_id).unwrap(); + let locked_other_mailbox = + mailboxes.get_mut(new_receipt.receiver_id()).unwrap(); locked_other_mailbox.incoming_receipts.push(new_receipt); } group.mailboxes.1.notify_all(); @@ -421,9 +422,9 @@ macro_rules! assert_receipts { $($action_name:ident, $action_pat:pat, $action_assert:block ),+ => [ $($produced_receipt:ident),*] ) => { let r = $group.get_receipt($to, $receipt); - assert_eq!(r.predecessor_id, $from); - assert_eq!(r.receiver_id, $to); - match &r.receipt { + assert_eq!(r.predecessor_id().clone(), $from); + assert_eq!(r.receiver_id().clone(), $to); + match r.receipt() { $receipt_pat => { $receipt_assert tuplet!(( $($action_name),* ) = $actions_name, "Incorrect number of actions"); diff --git a/runtime/runtime/tests/test_async_calls.rs b/runtime/runtime/tests/test_async_calls.rs index e8fe09ed609..d849cefd239 100644 --- a/runtime/runtime/tests/test_async_calls.rs +++ b/runtime/runtime/tests/test_async_calls.rs @@ -37,6 +37,7 @@ fn test_simple_func_call() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -83,6 +84,7 @@ fn test_single_promise_no_callback() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -149,6 +151,7 @@ fn test_single_promise_with_callback() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -233,6 +236,7 @@ fn test_two_promises_no_callbacks() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -327,6 +331,7 @@ fn test_two_promises_with_two_callbacks() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -418,6 +423,7 @@ fn test_single_promise_no_callback_batch() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -490,6 +496,7 @@ fn test_single_promise_with_callback_batch() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -564,6 +571,7 @@ fn test_simple_transfer() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -631,6 +639,7 @@ fn test_create_account_with_transfer_and_full_key() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -743,6 +752,7 @@ fn test_account_factory() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -889,6 +899,7 @@ fn test_create_account_add_key_call_delete_key_delete_account() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -983,6 +994,7 @@ fn test_transfer_64len_hex() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); @@ -1049,6 +1061,7 @@ fn test_create_transfer_64len_hex_fail() { deposit: 0, }))], CryptoHash::default(), + 0, ); let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); diff --git a/test-utils/runtime-tester/src/run_test.rs b/test-utils/runtime-tester/src/run_test.rs index c885a525c65..56540ba0b06 100644 --- a/test-utils/runtime-tester/src/run_test.rs +++ b/test-utils/runtime-tester/src/run_test.rs @@ -185,6 +185,7 @@ impl TransactionConfig { &self.signer, self.actions.clone(), *last_block.hash(), + 0, ) } } diff --git a/tools/fork-network/src/single_shard_storage_mutator.rs b/tools/fork-network/src/single_shard_storage_mutator.rs index 7206dda2e91..70bf695c689 100644 --- a/tools/fork-network/src/single_shard_storage_mutator.rs +++ b/tools/fork-network/src/single_shard_storage_mutator.rs @@ -94,8 +94,8 @@ impl SingleShardStorageMutator { pub(crate) fn set_postponed_receipt(&mut self, receipt: &Receipt) -> anyhow::Result<()> { self.set( TrieKey::PostponedReceipt { - receiver_id: receipt.receiver_id.clone(), - receipt_id: receipt.receipt_id, + receiver_id: receipt.receiver_id().clone(), + receipt_id: *receipt.receipt_id(), }, borsh::to_vec(&receipt)?, ) @@ -103,8 +103,8 @@ impl SingleShardStorageMutator { pub(crate) fn delete_postponed_receipt(&mut self, receipt: &Receipt) -> anyhow::Result<()> { self.remove(TrieKey::PostponedReceipt { - receiver_id: receipt.receiver_id.clone(), - receipt_id: receipt.receipt_id, + receiver_id: receipt.receiver_id().clone(), + receipt_id: *receipt.receipt_id(), }) } diff --git a/tools/mirror/src/chain_tracker.rs b/tools/mirror/src/chain_tracker.rs index a232ce0802d..7cd21994d2a 100644 --- a/tools/mirror/src/chain_tracker.rs +++ b/tools/mirror/src/chain_tracker.rs @@ -45,14 +45,14 @@ impl TxSendInfo { target_height: BlockHeight, now: Instant, ) -> Self { - let target_signer_id = if &tx.source_signer_id != &tx.target_tx.transaction.signer_id { - Some(tx.target_tx.transaction.signer_id.clone()) + let target_signer_id = if &tx.source_signer_id != tx.target_tx.transaction.signer_id() { + Some(tx.target_tx.transaction.signer_id().clone()) } else { None }; - let target_receiver_id = if &tx.source_receiver_id != &tx.target_tx.transaction.receiver_id + let target_receiver_id = if &tx.source_receiver_id != tx.target_tx.transaction.receiver_id() { - Some(tx.target_tx.transaction.receiver_id.clone()) + Some(tx.target_tx.transaction.receiver_id().clone()) } else { None }; @@ -68,7 +68,7 @@ impl TxSendInfo { actions: tx .target_tx .transaction - .actions + .actions() .iter() .map(|a| a.as_ref().to_string()) .collect::>(), @@ -421,8 +421,8 @@ impl TxTracker { let info = self .nonces .get_mut(&( - tx.target_tx.transaction.signer_id.clone(), - tx.target_tx.transaction.public_key.clone(), + tx.target_tx.transaction.signer_id().clone(), + tx.target_tx.transaction.public_key().clone(), )) .unwrap(); info.queued_txs.insert(tx_ref.clone()); @@ -439,8 +439,8 @@ impl TxTracker { let info = self .nonces .get_mut(&( - tx.target_tx.signer_id.clone(), - tx.target_tx.public_key.clone(), + tx.target_tx.signer_id().clone(), + tx.target_tx.public_key().clone(), )) .unwrap(); info.txs_awaiting_nonce.insert(tx_ref.clone()); @@ -481,8 +481,10 @@ impl TxTracker { for c in self.queued_blocks[0].chunks.iter_mut() { for tx in c.txs.iter_mut() { if let TargetChainTx::AwaitingNonce(t) = tx { - needed_access_keys - .insert((t.target_tx.signer_id.clone(), t.target_tx.public_key.clone())); + needed_access_keys.insert(( + t.target_tx.signer_id().clone(), + t.target_tx.public_key().clone(), + )); } } } @@ -505,12 +507,12 @@ impl TxTracker { TargetChainTx::Ready(t) => { tracing::debug!( target: "mirror", "Prepared {} for ({}, {:?}) with nonce {} even though there are still pending outcomes that may affect the access key", - &t.provenance, &t.target_tx.transaction.signer_id, &t.target_tx.transaction.public_key, t.target_tx.transaction.nonce + &t.provenance, t.target_tx.transaction.signer_id(), t.target_tx.transaction.public_key(), t.target_tx.transaction.nonce() ); self.nonces .get_mut(&( - t.target_tx.transaction.signer_id.clone(), - t.target_tx.transaction.public_key.clone(), + t.target_tx.transaction.signer_id().clone(), + t.target_tx.transaction.public_key().clone(), )) .unwrap() .txs_awaiting_nonce @@ -519,12 +521,12 @@ impl TxTracker { TargetChainTx::AwaitingNonce(t) => { tracing::warn!( target: "mirror", "Could not prepare {} for ({}, {:?}). Nonce unknown", - &t.provenance, &t.target_tx.signer_id, &t.target_tx.public_key, + &t.provenance, t.target_tx.signer_id(), t.target_tx.public_key(), ); self.nonces .get_mut(&( - t.target_tx.signer_id.clone(), - t.target_tx.public_key.clone(), + t.target_tx.signer_id().clone(), + t.target_tx.public_key().clone(), )) .unwrap() .txs_awaiting_nonce @@ -750,7 +752,7 @@ impl TxTracker { tx.try_set_nonce(nonce); match tx { TargetChainTx::Ready(t) => { - tracing::debug!(target: "mirror", "set nonce for {:?}'s {} to {}", access_key, r, t.target_tx.transaction.nonce); + tracing::debug!(target: "mirror", "set nonce for {:?}'s {} to {}", access_key, r, t.target_tx.transaction.nonce()); } _ => { tracing::warn!(target: "mirror", "Couldn't set nonce for {:?}'s {}", access_key, r); @@ -929,12 +931,12 @@ impl TxTracker { { tracing::debug!( target: "mirror", "Successfully sent transaction {} for {}: {:?}", - &hash, &tx.provenance, &tx.target_tx.transaction.actions, + &hash, &tx.provenance, tx.target_tx.transaction.actions(), ); } let access_key = ( - tx.target_tx.transaction.signer_id.clone(), - tx.target_tx.transaction.public_key.clone(), + tx.target_tx.transaction.signer_id().clone(), + tx.target_tx.transaction.public_key().clone(), ); let source_height = tx_ref.as_ref().map(|t| t.source_height); // TODO: don't keep adding txs if we're not ever finding them on chain, since we'll OOM eventually @@ -943,14 +945,14 @@ impl TxTracker { let txs = self.txs_by_signer.entry(access_key.clone()).or_default(); if let Some(highest_nonce) = txs.iter().next_back() { - if highest_nonce.nonce > tx.target_tx.transaction.nonce { + if highest_nonce.nonce > tx.target_tx.transaction.nonce() { tracing::warn!( target: "mirror", "transaction sent with out of order nonce: {}: {}. Sent so far: {:?}", - &hash, tx.target_tx.transaction.nonce, txs + &hash, tx.target_tx.transaction.nonce(), txs ); } } - if !txs.insert(TxId { hash, nonce: tx.target_tx.transaction.nonce }) { + if !txs.insert(TxId { hash, nonce: tx.target_tx.transaction.nonce() }) { tracing::warn!(target: "mirror", "inserted tx {} twice into txs_by_signer", &hash); } @@ -999,15 +1001,15 @@ impl TxTracker { let mut t = crate::read_target_nonce( db, - &tx.target_tx.transaction.signer_id, - &tx.target_tx.transaction.public_key, + tx.target_tx.transaction.signer_id(), + tx.target_tx.transaction.public_key(), )? .unwrap(); - t.nonce = std::cmp::max(t.nonce, Some(tx.target_tx.transaction.nonce)); + t.nonce = std::cmp::max(t.nonce, Some(tx.target_tx.transaction.nonce())); crate::put_target_nonce( db, - &tx.target_tx.transaction.signer_id, - &tx.target_tx.transaction.public_key, + tx.target_tx.transaction.signer_id(), + tx.target_tx.transaction.public_key(), &t, )?; let info = self.nonces.get_mut(&access_key).unwrap(); @@ -1101,7 +1103,7 @@ impl TxTracker { target_tx.try_set_nonce(None); match target_tx { TargetChainTx::Ready(t) => { - tracing::debug!(target: "mirror", "After skipping {} setting nonce for {:?}'s {} to {}", tx_ref, &access_key, r, t.target_tx.transaction.nonce); + tracing::debug!(target: "mirror", "After skipping {} setting nonce for {:?}'s {} to {}", tx_ref, &access_key, r, t.target_tx.transaction.nonce()); } _ => { tracing::warn!(target: "mirror", "After skipping {} could not set nonce for {:?}'s {}", tx_ref, &access_key, r); @@ -1125,7 +1127,7 @@ impl TxTracker { } } } - let access_key = (tx.signer_id.clone(), tx.public_key.clone()); + let access_key = (tx.signer_id().clone(), tx.public_key().clone()); let info = self.nonces.get_mut(&access_key).unwrap(); if info.last_height <= Some(tx_ref.source_height) { access_keys_to_remove.insert(access_key); diff --git a/tools/mirror/src/genesis.rs b/tools/mirror/src/genesis.rs index a84d05d2eb2..dd6f0382ed4 100644 --- a/tools/mirror/src/genesis.rs +++ b/tools/mirror/src/genesis.rs @@ -155,9 +155,9 @@ pub fn map_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 { + receipt.set_predecessor_id(crate::key_mapping::map_account(receipt.predecessor_id(), secret)); + receipt.set_receiver_id(crate::key_mapping::map_account(receipt.receiver_id(), secret)); + match receipt.receipt_mut() { ReceiptEnum::Action(r) | ReceiptEnum::PromiseYield(r) => { map_action_receipt(r, secret, default_key); } @@ -268,7 +268,7 @@ mod test { use near_primitives::account::{AccessKeyPermission, FunctionCallPermission}; use near_primitives::action::delegate::{DelegateAction, SignedDelegateAction}; use near_primitives::hash::CryptoHash; - use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; + use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum, ReceiptV0}; use near_primitives::transaction::{Action, AddKeyAction, CreateAccountAction}; use near_primitives_core::account::AccessKey; @@ -276,7 +276,7 @@ mod test { fn test_map_receipt() { let default_key = crate::key_mapping::default_extra_key(None).public_key(); - let mut receipt0 = Receipt { + let mut receipt0 = Receipt::V0(ReceiptV0 { predecessor_id: "foo.near".parse().unwrap(), receiver_id: "foo.foo.near".parse().unwrap(), receipt_id: CryptoHash::default(), @@ -305,8 +305,8 @@ mod test { })), ], }), - }; - let want_receipt0 = Receipt { + }); + let want_receipt0 = Receipt::V0(ReceiptV0 { predecessor_id: "foo.near".parse().unwrap(), receiver_id: "foo.foo.near".parse().unwrap(), receipt_id: CryptoHash::default(), @@ -339,7 +339,7 @@ mod test { })), ], }), - }; + }); let secret_key = SecretKey::from_random(KeyType::ED25519); let delegate_action = DelegateAction { @@ -360,7 +360,7 @@ mod test { let tx_hash = delegate_action.get_nep461_hash(); let signature = secret_key.sign(tx_hash.as_ref()); - let mut receipt1 = Receipt { + let mut receipt1 = Receipt::V0(ReceiptV0 { predecessor_id: "757a45019f9a3e5bd475586c31f63d6e15d50f5366caf4643f6f69731a222cad" .parse() .unwrap(), @@ -383,7 +383,7 @@ mod test { signature, }))], }), - }; + }); let mapped_secret_key = crate::key_mapping::map_key(&secret_key.public_key(), None); let delegate_action = DelegateAction { @@ -403,7 +403,7 @@ mod test { }; let tx_hash = delegate_action.get_nep461_hash(); let signature = mapped_secret_key.sign(tx_hash.as_ref()); - let want_receipt1 = Receipt { + let want_receipt1 = Receipt::V0(ReceiptV0 { predecessor_id: "3f8c3be8929e5fa61907f13a6247e7e452b92bb7d224cf691a9aa67814eb509b" .parse() .unwrap(), @@ -426,7 +426,7 @@ mod test { signature, }))], }), - }; + }); crate::genesis::map_receipt(&mut receipt0, None, &default_key); assert_eq!(receipt0, want_receipt0); diff --git a/tools/mirror/src/lib.rs b/tools/mirror/src/lib.rs index 49d32eae816..4cf82668d4b 100644 --- a/tools/mirror/src/lib.rs +++ b/tools/mirror/src/lib.rs @@ -261,28 +261,28 @@ impl SourceTransaction { fn public_key(&self) -> &PublicKey { match self { - Self::Tx(tx) => &tx.transaction.public_key, + Self::Tx(tx) => tx.transaction.public_key(), Self::TxView(tx) => &tx.public_key, } } fn signer_id(&self) -> &AccountId { match self { - Self::Tx(tx) => &tx.transaction.signer_id, + Self::Tx(tx) => tx.transaction.signer_id(), Self::TxView(tx) => &tx.signer_id, } } fn receiver_id(&self) -> &AccountId { match self { - Self::Tx(tx) => &tx.transaction.receiver_id, + Self::Tx(tx) => tx.transaction.receiver_id(), Self::TxView(tx) => &tx.receiver_id, } } fn actions<'a>(&'a self) -> Cow<'a, [Action]> { match self { - Self::Tx(tx) => Cow::Borrowed(&tx.transaction.actions), + Self::Tx(tx) => Cow::Borrowed(tx.transaction.actions()), Self::TxView(tx) => { Cow::Owned(tx.actions.iter().map(|a| a.clone().try_into().unwrap()).collect()) } @@ -597,9 +597,15 @@ impl TxAwaitingNonce { provenance: MappedTxProvenance, nonce_updates: HashSet<(AccountId, PublicKey)>, ) -> Self { - let mut target_tx = - Transaction::new(target_signer_id, target_public_key, target_receiver_id, 0, *ref_hash); - target_tx.actions = actions; + let mut target_tx = Transaction::new( + target_signer_id, + target_public_key, + target_receiver_id, + 0, + *ref_hash, + 0, + ); + *target_tx.actions_mut() = actions; Self { source_signer_id, source_receiver_id, @@ -645,8 +651,9 @@ impl MappedTx { target_receiver_id, nonce, *ref_hash, + 0, ); - target_tx.actions = actions; + *target_tx.actions_mut() = actions; let target_tx = SignedTransaction::new( target_secret_key.sign(&target_tx.get_hash_and_size().0.as_ref()), target_tx, @@ -663,7 +670,7 @@ impl MappedTx { fn inc_nonce(&mut self, target_secret_key: &SecretKey) { let mut tx = self.target_tx.transaction.clone(); - tx.nonce += 1; + *tx.nonce_mut() += 1; self.target_tx = SignedTransaction::new(target_secret_key.sign(&tx.get_hash_and_size().0.as_ref()), tx); } @@ -679,7 +686,7 @@ impl TargetChainTx { fn set_nonce(&mut self, nonce: Nonce) { match self { Self::AwaitingNonce(t) => { - t.target_tx.nonce = nonce; + *t.target_tx.nonce_mut() = nonce; let target_tx = SignedTransaction::new( t.target_secret_key.sign(&t.target_tx.get_hash_and_size().0.as_ref()), t.target_tx.clone(), @@ -769,7 +776,7 @@ impl TargetChainTx { fn target_nonce(&self) -> TargetNonce { match self { Self::Ready(t) => TargetNonce { - nonce: Some(t.target_tx.transaction.nonce), + nonce: Some(t.target_tx.transaction.nonce()), pending_outcomes: HashSet::new(), }, Self::AwaitingNonce(t) => t.target_nonce.clone(), @@ -938,7 +945,7 @@ impl TxMirror { // only once instance of this code will run, but this is the place to detect if that's not the case. tracing::error!( target: "mirror", "Tried to send an invalid tx for ({}, {:?}) from {}: {:?}", - &tx.target_tx.transaction.signer_id, &tx.target_tx.transaction.public_key, &tx.provenance, e + tx.target_tx.transaction.signer_id(), tx.target_tx.transaction.public_key(), &tx.provenance, e ); crate::metrics::TRANSACTIONS_SENT.with_label_values(&["invalid"]).inc(); } @@ -957,7 +964,7 @@ impl TxMirror { // TODO: here we should just save this transaction for later and send it when it's known tracing::warn!( target: "mirror", "skipped sending transaction for ({}, {:?}) because valid target chain nonce not known", - &tx.target_tx.signer_id, &tx.target_tx.public_key + tx.target_tx.signer_id(), tx.target_tx.public_key() ); } } @@ -1312,10 +1319,11 @@ impl TxMirror { Err(ChainError::Other(e)) => return Err(e), }; - if let ReceiptEnum::Action(r) | ReceiptEnum::PromiseYield(r) = &receipt.receipt { - if (provenance.is_create_account() && receipt.predecessor_id == receipt.receiver_id) + if let ReceiptEnum::Action(r) | ReceiptEnum::PromiseYield(r) = receipt.receipt() { + if (provenance.is_create_account() + && receipt.predecessor_id() == receipt.receiver_id()) || (!provenance.is_create_account() - && receipt.predecessor_id != receipt.receiver_id) + && receipt.predecessor_id() != receipt.receiver_id()) { continue; } @@ -1341,19 +1349,19 @@ impl TxMirror { if account_created { tracing::warn!( target: "mirror", "for receipt {} predecessor and receiver are the same but there's a create account in the actions: {:?}", - &receipt.receipt_id, &r.actions, + receipt.receipt_id(), &r.actions, ); } } let outcome = self .source_chain_access .get_outcome(TransactionOrReceiptId::Receipt { - receipt_id: receipt.receipt_id, - receiver_id: receipt.receiver_id.clone(), + receipt_id: *receipt.receipt_id(), + receiver_id: receipt.receiver_id().clone(), }) .await .with_context(|| { - format!("failed fetching outcome for receipt {}", receipt.receipt_id) + format!("failed fetching outcome for receipt {}", receipt.receipt_id()) })?; if !execution_status_good(&outcome.outcome.status) { continue; @@ -1363,8 +1371,8 @@ impl TxMirror { tracker, outcome.block_hash, txs, - receipt.predecessor_id.clone(), - receipt.receiver_id.clone(), + receipt.predecessor_id().clone(), + receipt.receiver_id().clone(), &r.actions, ref_hash, provenance, @@ -1426,13 +1434,13 @@ impl TxMirror { tracker: &mut crate::chain_tracker::TxTracker, txs: &mut Vec, ) -> anyhow::Result<()> { - if let ReceiptEnum::Action(r) | ReceiptEnum::PromiseYield(r) = &receipt.receipt { + if let ReceiptEnum::Action(r) | ReceiptEnum::PromiseYield(r) = receipt.receipt() { if r.actions.iter().any(|a| matches!(a, Action::FunctionCall(_))) { self.add_function_call_keys( tracker, txs, - &receipt.receipt_id, - &receipt.receiver_id, + receipt.receipt_id(), + receipt.receiver_id(), ref_hash, provenance, source_height, diff --git a/tools/state-viewer/src/apply_chain_range.rs b/tools/state-viewer/src/apply_chain_range.rs index 046aca676a5..54b5e8bace2 100644 --- a/tools/state-viewer/src/apply_chain_range.rs +++ b/tools/state-viewer/src/apply_chain_range.rs @@ -222,7 +222,7 @@ fn apply_block_from_range( if only_contracts { let mut has_contracts = false; for tx in chunk.transactions() { - for action in &tx.transaction.actions { + for action in tx.transaction.actions() { has_contracts = has_contracts || matches!(action, Action::FunctionCall(_) | Action::DeployContract(_)); } diff --git a/tools/state-viewer/src/apply_chunk.rs b/tools/state-viewer/src/apply_chunk.rs index a8ca73b051b..9f12d4c66fb 100644 --- a/tools/state-viewer/src/apply_chunk.rs +++ b/tools/state-viewer/src/apply_chunk.rs @@ -189,7 +189,7 @@ fn find_tx_or_receipt( let shard_layout = epoch_manager.get_shard_layout_from_prev_block(chunk.prev_block())?; let to_shard = - shard_layout::account_id_to_shard_id(&receipt.receiver_id, &shard_layout); + shard_layout::account_id_to_shard_id(receipt.receiver_id(), &shard_layout); return Ok(Some((HashType::Receipt, to_shard))); } } @@ -397,7 +397,7 @@ fn apply_receipt_in_chunk( let shard_layout = epoch_manager.get_shard_layout_from_prev_block(chunk.prev_block())?; let to_shard = shard_layout::account_id_to_shard_id( - &receipt.receiver_id, + receipt.receiver_id(), &shard_layout, ); to_apply.insert((height, to_shard)); @@ -684,7 +684,7 @@ mod test { for receipt in chunk.prev_outgoing_receipts() { let to_shard = shard_layout::account_id_to_shard_id( - &receipt.receiver_id, + receipt.receiver_id(), &shard_layout, ); diff --git a/tools/state-viewer/src/contract_accounts.rs b/tools/state-viewer/src/contract_accounts.rs index 025fe5b4719..fce79dca2e1 100644 --- a/tools/state-viewer/src/contract_accounts.rs +++ b/tools/state-viewer/src/contract_accounts.rs @@ -295,16 +295,16 @@ fn try_find_actions_spawned_by_receipt( .map_err(|e| ContractAccountError::InvalidReceipt(e, key))?; // Skip refunds. - if receipt.receiver_id.is_system() { + if receipt.receiver_id().is_system() { return Ok(()); } // Note: We could use the entry API here to avoid the double hash, but we // would have to clone the key string. It's unclear which is better, I will // avoid the entry API because normal contains/get_mut seems simpler. - if accounts.contains_key(&receipt.receiver_id) { + if accounts.contains_key(receipt.receiver_id()) { // yes, this is a contract in our map (skip/select filtering has already been applied when constructing the map) - let entry = accounts.get_mut(&receipt.receiver_id).unwrap(); + let entry = accounts.get_mut(receipt.receiver_id()).unwrap(); if filter.receipts_in { *entry.receipts_in.get_or_insert(0) += 1; } @@ -327,7 +327,7 @@ fn try_find_actions_spawned_by_receipt( let outgoing_receipt = maybe_outgoing_receipt.ok_or_else(|| { ContractAccountError::MissingOutgoingReceipt(*outgoing_receipt_id) })?; - match outgoing_receipt.receipt { + match outgoing_receipt.receipt() { ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => { for action in &action_receipt.actions { @@ -493,7 +493,7 @@ mod tests { use borsh::BorshSerialize; use near_crypto::{InMemorySigner, Signer}; use near_primitives::hash::CryptoHash; - use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; + use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum, ReceiptV0}; use near_primitives::transaction::{ Action, CreateAccountAction, DeployContractAction, ExecutionMetadata, ExecutionOutcome, ExecutionOutcomeWithProof, ExecutionStatus, FunctionCallAction, TransferAction, @@ -722,7 +722,7 @@ mod tests { let sender_id: AccountId = sender.parse().unwrap(); let signer = InMemorySigner::from_seed(sender_id.clone(), near_crypto::KeyType::ED25519, "seed"); - Receipt { + Receipt::V0(ReceiptV0 { predecessor_id: sender_id.clone(), receiver_id: receiver.parse().unwrap(), receipt_id: CryptoHash::default(), @@ -734,6 +734,6 @@ mod tests { input_data_ids: vec![], actions, }), - } + }) } } diff --git a/tools/state-viewer/src/state_dump.rs b/tools/state-viewer/src/state_dump.rs index 4a6e68732a3..4328bc0e281 100644 --- a/tools/state-viewer/src/state_dump.rs +++ b/tools/state-viewer/src/state_dump.rs @@ -458,6 +458,7 @@ mod test { code: near_test_contracts::backwards_compatible_rs_contract().to_vec(), })], genesis_hash, + 0, ); let tx01 = SignedTransaction::stake( 1, diff --git a/tools/state-viewer/src/tx_dump.rs b/tools/state-viewer/src/tx_dump.rs index e69bee4d1a1..291f18f7f5c 100644 --- a/tools/state-viewer/src/tx_dump.rs +++ b/tools/state-viewer/src/tx_dump.rs @@ -35,6 +35,6 @@ fn should_include_signed_transaction( ) -> bool { match select_account_ids { None => true, - Some(specified_ids) => specified_ids.contains(&signed_transaction.transaction.receiver_id), + Some(specified_ids) => specified_ids.contains(signed_transaction.transaction.receiver_id()), } }