From 91f2588c17893da0d47f3d4660018b1c2ffa35f9 Mon Sep 17 00:00:00 2001 From: Jakob Meier Date: Tue, 19 Jul 2022 18:11:26 +0200 Subject: [PATCH 1/3] refactor: `call_main` utility for integration test We have been copying the same code multiple times, this refactor pulls out the functionality the `TestEnv`. --- chain/client/src/test_utils.rs | 63 +++++-- .../src/tests/client/process_blocks.rs | 167 ++++-------------- 2 files changed, 83 insertions(+), 147 deletions(-) diff --git a/chain/client/src/test_utils.rs b/chain/client/src/test_utils.rs index d32930fd304..33124736a6d 100644 --- a/chain/client/src/test_utils.rs +++ b/chain/client/src/test_utils.rs @@ -22,7 +22,7 @@ use near_chain::{ Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode, Provenance, RuntimeAdapter, }; use near_chain_configs::ClientConfig; -use near_crypto::{InMemorySigner, KeyType, PublicKey}; +use near_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; use near_network::test_utils::{MockPeerManagerAdapter, NetworkRecipient}; use near_network::types::{ FullPeerInfo, NetworkClientMessages, NetworkClientResponses, NetworkRequests, NetworkResponses, @@ -36,7 +36,7 @@ use near_primitives::merkle::{merklize, MerklePath, PartialMerkleTree}; use near_primitives::receipt::Receipt; use near_primitives::shard_layout::ShardUId; use near_primitives::sharding::{EncodedShardChunk, PartialEncodedChunk, ReedSolomonWrapper}; -use near_primitives::transaction::SignedTransaction; +use near_primitives::transaction::{Action, FunctionCallAction, SignedTransaction, Transaction}; use near_primitives::types::{ AccountId, Balance, BlockHeight, BlockHeightDelta, EpochId, NumBlocks, NumSeats, NumShards, ShardId, @@ -1511,16 +1511,6 @@ impl TestEnv { } } - #[track_caller] - pub fn query_transaction_status( - &mut self, - transaction_hash: &CryptoHash, - ) -> FinalExecutionOutcomeView { - self.clients[0].chain.get_final_transaction_result(transaction_hash).unwrap_or_else(|err| { - panic!("failed to get transaction status for {}: {}", transaction_hash, err) - }) - } - pub fn query_balance(&mut self, account_id: AccountId) -> Balance { self.query_account(account_id).amount } @@ -1558,6 +1548,55 @@ impl TestEnv { pub fn get_runtime_config(&self, idx: usize, epoch_id: EpochId) -> RuntimeConfig { self.clients[idx].runtime_adapter.get_protocol_config(&epoch_id).unwrap().runtime_config } + + /// Create and process a SIR tx from actions and return the execution outcome. + /// + /// The signer defines both sender and receiver of the transaction, hence it + /// is a Sender-is-Receiver transaction. + pub fn execute_sir_tx( + &mut self, + actions: Vec, + signer: &InMemorySigner, + ) -> FinalExecutionOutcomeView { + let tip = self.clients[0].chain.head().unwrap(); + let tx = Transaction { + signer_id: signer.account_id.clone(), + receiver_id: signer.account_id.clone(), + public_key: signer.public_key(), + actions, + nonce: tip.height + 1, + block_hash: tip.last_block_hash, + }; + let signed_tx = tx.sign(signer); + let tx_hash = signed_tx.get_hash().clone(); + self.clients[0].process_tx(signed_tx, false, false); + let max_iters = 1000; + for i in 0..max_iters { + let block = self.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); + self.process_block(0, block.clone(), Provenance::PRODUCED); + if let Ok(outcome) = self.clients[0].chain.get_final_transaction_result(&tx_hash) { + return outcome; + } + } + panic!("No transaction outcome found after {max_iters} blocks.") + } + + /// Execute a function call transaction that calls main on the `TestEnv`. + /// + /// This function assumes that account has been deployed and that + /// `InMemorySigner::from_seed` produces a valid signer that has it's key + /// deployed already. + pub fn call_main(&mut self, account: &AccountId) -> FinalExecutionOutcomeView { + let signer = InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_str()); + let actions = vec![Action::FunctionCall(FunctionCallAction { + method_name: "main".to_string(), + args: vec![], + gas: 3 * 10u64.pow(14), + deposit: 0, + })]; + + self.execute_sir_tx(actions, &signer) + } } pub fn create_chunk_on_height_for_shard( diff --git a/integration-tests/src/tests/client/process_blocks.rs b/integration-tests/src/tests/client/process_blocks.rs index b9ff865e05c..727925d2e7e 100644 --- a/integration-tests/src/tests/client/process_blocks.rs +++ b/integration-tests/src/tests/client/process_blocks.rs @@ -228,6 +228,23 @@ fn prepare_env_with_congestion( (env, tx_hashes) } +/// Create a `TestEnv` with an account and a contract deployed to that account. +fn prepare_env_with_contract( + epoch_length: u64, + protocol_version: u32, + account: AccountId, + contract: Vec, +) -> TestEnv { + let mut genesis = Genesis::test(vec![account.clone()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = protocol_version; + let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) + .runtime_adapters(create_nightshade_runtimes(&genesis, 1)) + .build(); + deploy_test_contract(&mut env, account, &contract, epoch_length.clone(), 1); + env +} + /// Runs block producing client and stops after network mock received two blocks. #[test] fn produce_two_blocks() { @@ -2709,32 +2726,7 @@ fn test_execution_metadata() { }; // Call the contract and get the execution outcome. - let execution_outcome = { - let tip = env.clients[0].chain.head().unwrap(); - let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); - let tx = Transaction { - signer_id: "test0".parse().unwrap(), - receiver_id: "test0".parse().unwrap(), - public_key: signer.public_key(), - actions: vec![Action::FunctionCall(FunctionCallAction { - method_name: "main".to_string(), - args: Vec::new(), - gas: 100_000_000_000_000, - deposit: 0, - })], - - nonce: 10, - block_hash: tip.last_block_hash, - } - .sign(&signer); - let tx_hash = tx.get_hash(); - - env.clients[0].process_tx(tx, false, false); - for i in 0..3 { - env.produce_block(0, tip.height + i + 1); - } - env.query_transaction_status(&tx_hash) - }; + let execution_outcome = env.call_main(&"test0".parse().unwrap()); // Now, let's assert that we get the cost breakdown we expect. let config = RuntimeConfigStore::test().get_config(PROTOCOL_VERSION).clone(); @@ -3358,49 +3350,12 @@ fn verify_contract_limits_upgrade( env }; - let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); - let tx = Transaction { - signer_id: "test0".parse().unwrap(), - receiver_id: "test0".parse().unwrap(), - public_key: signer.public_key(), - actions: vec![Action::FunctionCall(FunctionCallAction { - method_name: "main".to_string(), - args: Vec::new(), - gas: 100_000_000_000_000, - deposit: 0, - })], - - nonce: 0, - block_hash: CryptoHash::default(), - }; - - // Run the transaction & get tx outcome. - let old_outcome = { - let tip = env.clients[0].chain.head().unwrap(); - let signed_transaction = - Transaction { nonce: 10, block_hash: tip.last_block_hash, ..tx.clone() }.sign(&signer); - let tx_hash = signed_transaction.get_hash(); - env.clients[0].process_tx(signed_transaction, false, false); - for i in 0..3 { - env.produce_block(0, tip.height + i + 1); - } - env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() - }; + let account = "test0".parse().unwrap(); + let old_outcome = env.call_main(&account); env.upgrade_protocol(new_protocol_version); - // Re-run the transaction & get tx outcome. - let new_outcome = { - let tip = env.clients[0].chain.head().unwrap(); - let signed_transaction = - Transaction { nonce: 11, block_hash: tip.last_block_hash, ..tx }.sign(&signer); - let tx_hash = signed_transaction.get_hash(); - env.clients[0].process_tx(signed_transaction, false, false); - for i in 0..3 { - env.produce_block(0, tip.height + i + 1); - } - env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() - }; + let new_outcome = env.call_main(&account); assert_matches!(old_outcome.status, FinalExecutionStatus::SuccessValue(_)); let e = match new_outcome.status { @@ -4990,7 +4945,6 @@ mod lower_storage_key_limit_test { mod new_contract_loading_cost { use super::*; - use near_primitives::views::FinalExecutionOutcomeView; /// Check that normal execution has the same gas cost after FixContractLoadingCost. #[test] @@ -5005,18 +4959,20 @@ mod new_contract_loading_cost { let epoch_length: BlockHeight = 5; let account: AccountId = "test0".parse().unwrap(); - let mut env = - test_env_with_contract(epoch_length, old_protocol_version, account.clone(), contract); - - let signer = InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_str()); + let mut env = prepare_env_with_contract( + epoch_length, + old_protocol_version, + account.clone(), + contract, + ); - let old_result = call_main(&mut env, &signer, epoch_length); + let old_result = env.call_main(&account); let old_gas = old_result.receipts_outcome[0].outcome.gas_burnt; assert_matches!(old_result.status, FinalExecutionStatus::SuccessValue(_)); env.upgrade_protocol(new_protocol_version); - let new_result = call_main(&mut env, &signer, epoch_length); + let new_result = env.call_main(&account); let new_gas = new_result.receipts_outcome[0].outcome.gas_burnt; assert_matches!(new_result.status, FinalExecutionStatus::SuccessValue(_)); @@ -5036,22 +4992,20 @@ mod new_contract_loading_cost { let epoch_length: BlockHeight = 5; let account: AccountId = "test0".parse().unwrap(); - let mut env = test_env_with_contract( + let mut env = prepare_env_with_contract( epoch_length, old_protocol_version, account.clone(), bad_contract, ); - let signer = InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_str()); - - let old_result = call_main(&mut env, &signer, epoch_length); + let old_result = env.call_main(&account); let old_gas = old_result.receipts_outcome[0].outcome.gas_burnt; assert_matches!(old_result.status, FinalExecutionStatus::Failure(_)); env.upgrade_protocol(new_protocol_version); - let new_result = call_main(&mut env, &signer, epoch_length); + let new_result = env.call_main(&account); let new_gas = new_result.receipts_outcome[0].outcome.gas_burnt; assert_matches!(new_result.status, FinalExecutionStatus::Failure(_)); @@ -5063,61 +5017,4 @@ mod new_contract_loading_cost { let loading_cost = loading_base + contract_size as u64 * loading_byte; assert_eq!(old_gas + loading_cost, new_gas); } - - /// Create a `TestEnv` with a contract deployed for an account. - fn test_env_with_contract( - epoch_length: u64, - protocol_version: u32, - account: AccountId, - contract: Vec, - ) -> TestEnv { - let mut genesis = Genesis::test(vec![account], 1); - genesis.config.epoch_length = epoch_length; - genesis.config.protocol_version = protocol_version; - let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) - .runtime_adapters(create_nightshade_runtimes(&genesis, 1)) - .build(); - deploy_test_contract( - &mut env, - "test0".parse().unwrap(), - &contract, - epoch_length.clone(), - 1, - ); - env - } - - /// Execute a function call transaction that calls main on the `TestEnv`. - fn call_main( - env: &mut TestEnv, - signer: &InMemorySigner, - epoch_length: u64, - ) -> FinalExecutionOutcomeView { - let tx = Transaction { - signer_id: "test0".parse().unwrap(), - receiver_id: "test0".parse().unwrap(), - public_key: signer.public_key(), - actions: vec![Action::FunctionCall(FunctionCallAction { - method_name: "main".to_string(), - args: vec![], - gas: 3 * 10u64.pow(14), - deposit: 0, - })], - - nonce: 0, - block_hash: CryptoHash::default(), - }; - - let tip = env.clients[0].chain.head().unwrap(); - let signed_tx = - Transaction { nonce: tip.height + 1, block_hash: tip.last_block_hash, ..tx.clone() } - .sign(signer); - let tx_hash = signed_tx.get_hash().clone(); - env.clients[0].process_tx(signed_tx, false, false); - for i in 0..epoch_length { - let block = env.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); - env.process_block(0, block.clone(), Provenance::PRODUCED); - } - env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() - } } From 6bdfc46447e32ba25b84e8913baef0fc493c3f99 Mon Sep 17 00:00:00 2001 From: Jakob Meier Date: Wed, 20 Jul 2022 10:22:06 +0200 Subject: [PATCH 2/3] Split `execute_sir_tx` in two. --- chain/client/src/test_utils.rs | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/chain/client/src/test_utils.rs b/chain/client/src/test_utils.rs index 33124736a6d..5edc0138dc9 100644 --- a/chain/client/src/test_utils.rs +++ b/chain/client/src/test_utils.rs @@ -22,7 +22,7 @@ use near_chain::{ Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode, Provenance, RuntimeAdapter, }; use near_chain_configs::ClientConfig; -use near_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; +use near_crypto::{InMemorySigner, KeyType, PublicKey}; use near_network::test_utils::{MockPeerManagerAdapter, NetworkRecipient}; use near_network::types::{ FullPeerInfo, NetworkClientMessages, NetworkClientResponses, NetworkRequests, NetworkResponses, @@ -36,7 +36,7 @@ use near_primitives::merkle::{merklize, MerklePath, PartialMerkleTree}; use near_primitives::receipt::Receipt; use near_primitives::shard_layout::ShardUId; use near_primitives::sharding::{EncodedShardChunk, PartialEncodedChunk, ReedSolomonWrapper}; -use near_primitives::transaction::{Action, FunctionCallAction, SignedTransaction, Transaction}; +use near_primitives::transaction::{Action, FunctionCallAction, SignedTransaction}; use near_primitives::types::{ AccountId, Balance, BlockHeight, BlockHeightDelta, EpochId, NumBlocks, NumSeats, NumShards, ShardId, @@ -1549,28 +1549,30 @@ impl TestEnv { self.clients[idx].runtime_adapter.get_protocol_config(&epoch_id).unwrap().runtime_config } - /// Create and process a SIR tx from actions and return the execution outcome. - /// - /// The signer defines both sender and receiver of the transaction, hence it - /// is a Sender-is-Receiver transaction. - pub fn execute_sir_tx( + /// Create and sign transaction ready for execution. + pub fn tx_from_actions( &mut self, actions: Vec, signer: &InMemorySigner, - ) -> FinalExecutionOutcomeView { + receiver: AccountId, + ) -> SignedTransaction { let tip = self.clients[0].chain.head().unwrap(); - let tx = Transaction { - signer_id: signer.account_id.clone(), - receiver_id: signer.account_id.clone(), - public_key: signer.public_key(), + SignedTransaction::from_actions( + tip.height + 1, + signer.account_id.clone(), + receiver, + signer, actions, - nonce: tip.height + 1, - block_hash: tip.last_block_hash, - }; - let signed_tx = tx.sign(signer); - let tx_hash = signed_tx.get_hash().clone(); - self.clients[0].process_tx(signed_tx, false, false); - let max_iters = 1000; + tip.last_block_hash, + ) + } + + /// Process a tx and its receipts, then return the execution outcome. + pub fn execute_tx(&mut self, tx: SignedTransaction) -> FinalExecutionOutcomeView { + let tx_hash = tx.get_hash().clone(); + self.clients[0].process_tx(tx, false, false); + let max_iters = 100; + let tip = self.clients[0].chain.head().unwrap(); for i in 0..max_iters { let block = self.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); self.process_block(0, block.clone(), Provenance::PRODUCED); @@ -1594,8 +1596,8 @@ impl TestEnv { gas: 3 * 10u64.pow(14), deposit: 0, })]; - - self.execute_sir_tx(actions, &signer) + let tx = self.tx_from_actions(actions, &signer, signer.account_id.clone()); + self.execute_tx(tx) } } From af6f7611f3b599f43b1350649ae93e3d1fc3df56 Mon Sep 17 00:00:00 2001 From: Jakob Meier Date: Wed, 20 Jul 2022 10:28:39 +0200 Subject: [PATCH 3/3] de-duplicate code in test_deploy_cost_increased --- .../src/tests/client/process_blocks.rs | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/integration-tests/src/tests/client/process_blocks.rs b/integration-tests/src/tests/client/process_blocks.rs index 727925d2e7e..c6d09a48a37 100644 --- a/integration-tests/src/tests/client/process_blocks.rs +++ b/integration-tests/src/tests/client/process_blocks.rs @@ -3449,33 +3449,15 @@ fn test_deploy_cost_increased() { }; let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); - let tx = Transaction { - signer_id: "test0".parse().unwrap(), - receiver_id: "test0".parse().unwrap(), - public_key: signer.public_key(), - actions: vec![Action::DeployContract(DeployContractAction { code: test_contract })], - nonce: 0, - block_hash: CryptoHash::default(), - }; - - // Run the transaction & get tx outcome in a closure. - let deploy_contract = |env: &mut TestEnv, nonce: u64| { - let tip = env.clients[0].chain.head().unwrap(); - let signed_transaction = - Transaction { nonce, block_hash: tip.last_block_hash, ..tx.clone() }.sign(&signer); - let tx_hash = signed_transaction.get_hash(); - env.clients[0].process_tx(signed_transaction, false, false); - for i in 0..epoch_length { - env.produce_block(0, tip.height + i + 1); - } - env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap() - }; + let actions = vec![Action::DeployContract(DeployContractAction { code: test_contract })]; - let old_outcome = deploy_contract(&mut env, 10); + let tx = env.tx_from_actions(actions.clone(), &signer, signer.account_id.clone()); + let old_outcome = env.execute_tx(tx); env.upgrade_protocol(new_protocol_version); - let new_outcome = deploy_contract(&mut env, 11); + let tx = env.tx_from_actions(actions, &signer, signer.account_id.clone()); + let new_outcome = env.execute_tx(tx); assert_matches!(old_outcome.status, FinalExecutionStatus::SuccessValue(_)); assert_matches!(new_outcome.status, FinalExecutionStatus::SuccessValue(_));