diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 1f955b32887..15610c13c51 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -2712,6 +2712,7 @@ impl<'a> ChainUpdate<'a> { &challenges_result, *block.header().random_value(), true, + true, ) .unwrap(); let partial_state = apply_result.proof.unwrap().nodes; @@ -2864,6 +2865,7 @@ impl<'a> ChainUpdate<'a> { gas_limit, &block.header().challenges_result(), *block.header().random_value(), + true, ) .map_err(|e| ErrorKind::Other(e.to_string()))?; @@ -2918,6 +2920,7 @@ impl<'a> ChainUpdate<'a> { new_extra.gas_limit(), &block.header().challenges_result(), *block.header().random_value(), + false, ) .map_err(|e| ErrorKind::Other(e.to_string()))?; @@ -2925,6 +2928,19 @@ impl<'a> ChainUpdate<'a> { *new_extra.state_root_mut() = apply_result.new_root; self.chain_store_update.save_chunk_extra(&block.hash(), shard_id, new_extra); + + if !apply_result.outcomes.is_empty() { + // debug_assert!(false); + // Remove in next release + let (_, outcome_paths) = + ApplyTransactionResult::compute_outcomes_proof(&apply_result.outcomes); + self.chain_store_update.save_outcomes_with_proofs( + &block.hash(), + shard_id, + apply_result.outcomes, + outcome_paths, + ); + } } } } @@ -3612,6 +3628,7 @@ impl<'a> ChainUpdate<'a> { gas_limit, &block_header.challenges_result(), *block_header.random_value(), + true, )?; let (outcome_root, outcome_proofs) = @@ -3690,6 +3707,7 @@ impl<'a> ChainUpdate<'a> { chunk_extra.gas_limit(), &block_header.challenges_result(), *block_header.random_value(), + false, )?; self.chain_store_update.save_trie_changes(apply_result.trie_changes); diff --git a/chain/chain/src/test_utils.rs b/chain/chain/src/test_utils.rs index 7ed4bff83e0..0e9b35a83f5 100644 --- a/chain/chain/src/test_utils.rs +++ b/chain/chain/src/test_utils.rs @@ -603,6 +603,7 @@ impl RuntimeAdapter for KeyValueRuntime { _challenges: &ChallengesResult, _random_seed: CryptoHash, generate_storage_proof: bool, + _is_new_chunk: bool, ) -> Result { assert!(!generate_storage_proof); let mut tx_results = vec![]; @@ -781,6 +782,7 @@ impl RuntimeAdapter for KeyValueRuntime { _gas_limit: Gas, _challenges: &ChallengesResult, _random_value: CryptoHash, + _is_new_chunk: bool, ) -> Result { unimplemented!(); } diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index eb351744171..b36af33c1bf 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -515,6 +515,7 @@ pub trait RuntimeAdapter: Send + Sync { gas_limit: Gas, challenges_result: &ChallengesResult, random_seed: CryptoHash, + is_new_chunk: bool, ) -> Result { self.apply_transactions_with_optional_storage_proof( shard_id, @@ -531,6 +532,7 @@ pub trait RuntimeAdapter: Send + Sync { challenges_result, random_seed, false, + is_new_chunk, ) } @@ -550,6 +552,7 @@ pub trait RuntimeAdapter: Send + Sync { challenges_result: &ChallengesResult, random_seed: CryptoHash, generate_storage_proof: bool, + is_new_chunk: bool, ) -> Result; fn check_state_transition( @@ -568,6 +571,7 @@ pub trait RuntimeAdapter: Send + Sync { gas_limit: Gas, challenges_result: &ChallengesResult, random_value: CryptoHash, + is_new_chunk: bool, ) -> Result; /// Query runtime with given `path` and `data`. diff --git a/chain/chain/src/validate.rs b/chain/chain/src/validate.rs index 3113b603af1..0369edd411b 100644 --- a/chain/chain/src/validate.rs +++ b/chain/chain/src/validate.rs @@ -340,6 +340,8 @@ fn validate_chunk_state_challenge( prev_chunk_header.gas_limit(), &ChallengesResult::default(), *block_header.random_value(), + // TODO: set it properly when challenges are enabled + true, ) .map_err(|_| Error::from(ErrorKind::MaliciousChallenge))?; let outcome_root = ApplyTransactionResult::compute_outcomes_proof(&result.outcomes).0; diff --git a/chain/client/tests/process_blocks.rs b/chain/client/tests/process_blocks.rs index 5a4883ecc5d..55c3de1aae8 100644 --- a/chain/client/tests/process_blocks.rs +++ b/chain/client/tests/process_blocks.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{HashSet, VecDeque}; use std::iter::FromIterator; use std::path::Path; use std::str::FromStr; @@ -43,16 +43,22 @@ use near_primitives::sharding::{EncodedShardChunk, ReedSolomonWrapper, ShardChun #[cfg(feature = "protocol_feature_block_header_v3")] use near_primitives::sharding::{ShardChunkHeaderInner, ShardChunkHeaderV3}; +use near_primitives::receipt::DelayedReceiptIndices; use near_primitives::syncing::{get_num_state_parts, ShardStateSyncResponseHeader}; use near_primitives::transaction::{ - Action, DeployContractAction, FunctionCallAction, SignedTransaction, Transaction, + Action, DeployContractAction, ExecutionStatus, FunctionCallAction, SignedTransaction, + Transaction, }; +use near_primitives::trie_key::TrieKey; use near_primitives::types::validator_stake::ValidatorStake; use near_primitives::types::{AccountId, BlockHeight, EpochId, NumBlocks}; use near_primitives::utils::to_timestamp; use near_primitives::validator_signer::{InMemoryValidatorSigner, ValidatorSigner}; use near_primitives::version::PROTOCOL_VERSION; -use near_primitives::views::{BlockHeaderView, QueryRequest, QueryResponseKind}; +use near_primitives::views::{ + BlockHeaderView, FinalExecutionStatus, QueryRequest, QueryResponseKind, +}; +use near_store::get; use near_store::test_utils::create_test_store; use neard::config::{GenesisExt, TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; use neard::NEAR_BASE; @@ -2618,6 +2624,126 @@ fn test_block_ordinal() { assert_eq!(next_block.header().block_ordinal(), ordinal); } +#[test] +fn test_congestion_receipt_execution() { + init_test_logger(); + let epoch_length = 100; + let mut genesis = Genesis::test(vec!["test0", "test1"], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.gas_limit = 10000000000000; + let chain_genesis = ChainGenesis::from(&genesis); + let mut env = + TestEnv::new_with_runtime(chain_genesis, 1, 1, create_nightshade_runtimes(&genesis, 1)); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap().clone(); + let signer = InMemorySigner::from_seed("test0", KeyType::ED25519, "test0"); + let validator_signer = InMemoryValidatorSigner::from_seed("test0", KeyType::ED25519, "test0"); + + // step 0: deploy contract to test0 + let tx = SignedTransaction::from_actions( + 1, + "test0".to_string(), + "test0".to_string(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: near_test_contracts::rs_contract().to_vec(), + })], + *genesis_block.hash(), + ); + env.clients[0].process_tx(tx, false, false); + for i in 1..3 { + env.produce_block(0, i); + } + + // step 1: create function call transactions that generate promises + let gas_1 = 9_000_000_000_000; + let gas_2 = gas_1 / 3; + let mut tx_hashes = vec![]; + + for i in 0..3 { + let data = serde_json::json!([ + {"create": { + "account_id": "test0", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": gas_2, + }, "id": 0 } + ]); + + let signed_transaction = SignedTransaction::from_actions( + i + 10, + "test0".to_string(), + "test0".to_string(), + &signer, + vec![Action::FunctionCall(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: gas_1, + deposit: 0, + })], + *genesis_block.hash(), + ); + tx_hashes.push(signed_transaction.get_hash()); + env.clients[0].process_tx(signed_transaction, false, false); + } + + // step 2: produce block with no new chunk + env.produce_block(0, 3); + let height = 4; + env.produce_block(0, height); + let prev_block = env.clients[0].chain.get_block_by_height(height).unwrap().clone(); + let chunk_extra = env.clients[0].chain.get_chunk_extra(prev_block.hash(), 0).unwrap().clone(); + assert!(chunk_extra.gas_used() >= chunk_extra.gas_limit()); + let state_update = + env.clients[0].runtime_adapter.get_tries().new_trie_update(0, *chunk_extra.state_root()); + let delayed_indices = + get::(&state_update, &TrieKey::DelayedReceiptIndices) + .unwrap() + .unwrap(); + assert!(delayed_indices.next_available_index > 0); + let mut block = env.clients[0].produce_block(height + 1).unwrap().unwrap(); + let chunk_headers = vec![prev_block.chunks()[0].clone()]; + block.set_chunks(chunk_headers.clone()); + block.mut_header().get_mut().inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + block.mut_header().get_mut().inner_rest.chunk_tx_root = + Block::compute_chunk_tx_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.chunk_receipts_root = + Block::compute_chunk_receipts_root(&chunk_headers); + block.mut_header().get_mut().inner_lite.prev_state_root = + Block::compute_state_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.chunk_mask = vec![false]; + block.mut_header().get_mut().inner_rest.gas_price = prev_block.header().gas_price(); + block.mut_header().resign(&validator_signer); + env.process_block(0, block.clone(), Provenance::NONE); + + // let all receipts finish + for i in height + 2..height + 7 { + env.produce_block(0, i); + } + + for tx_hash in &tx_hashes { + let final_outcome = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert!(matches!(final_outcome.status, FinalExecutionStatus::SuccessValue(_))); + + // Check that all receipt ids have corresponding execution outcomes. This means that all receipts generated are executed. + let transaction_outcome = env.clients[0].chain.get_execution_outcome(tx_hash).unwrap(); + let mut receipt_ids: VecDeque<_> = + transaction_outcome.outcome_with_id.outcome.receipt_ids.into(); + while !receipt_ids.is_empty() { + let receipt_id = receipt_ids.pop_front().unwrap(); + let receipt_outcome = env.clients[0].chain.get_execution_outcome(&receipt_id).unwrap(); + match receipt_outcome.outcome_with_id.outcome.status { + ExecutionStatus::SuccessValue(_) | ExecutionStatus::SuccessReceiptId(_) => {} + ExecutionStatus::Failure(_) | ExecutionStatus::Unknown => { + panic!("unexpected receipt execution outcome") + } + } + receipt_ids.extend(receipt_outcome.outcome_with_id.outcome.receipt_ids); + } + } +} + #[cfg(feature = "protocol_feature_access_key_nonce_range")] #[cfg(test)] mod access_key_nonce_range_tests { diff --git a/core/primitives/src/runtime/apply_state.rs b/core/primitives/src/runtime/apply_state.rs index 39e814d5c08..e1dfefd2a91 100644 --- a/core/primitives/src/runtime/apply_state.rs +++ b/core/primitives/src/runtime/apply_state.rs @@ -34,6 +34,8 @@ pub struct ApplyState { pub config: Arc, /// Cache for compiled contracts. pub cache: Option>, + /// Whether the chunk being applied is new. + pub is_new_chunk: bool, /// Ethereum chain id. #[cfg(feature = "protocol_feature_evm")] pub evm_chain_id: u64, diff --git a/core/primitives/src/version.rs b/core/primitives/src/version.rs index f9b6a31cc4a..08576234581 100644 --- a/core/primitives/src/version.rs +++ b/core/primitives/src/version.rs @@ -13,7 +13,7 @@ pub struct Version { pub type DbVersion = u32; /// Current version of the database. -pub const DB_VERSION: DbVersion = 19; +pub const DB_VERSION: DbVersion = 20; /// Protocol version type. pub use near_primitives_core::types::ProtocolVersion; @@ -102,11 +102,12 @@ pub enum ProtocolFeature { TransactionSizeLimit, #[cfg(feature = "protocol_feature_allow_create_account_on_delete")] AllowCreateAccountOnDelete, + FixApplyChunks, } /// Current latest stable version of the protocol. #[cfg(not(feature = "nightly_protocol"))] -pub const PROTOCOL_VERSION: ProtocolVersion = 43; +pub const PROTOCOL_VERSION: ProtocolVersion = 44; /// Current latest nightly version of the protocol. #[cfg(feature = "nightly_protocol")] @@ -119,6 +120,7 @@ impl ProtocolFeature { #[cfg(feature = "protocol_feature_lower_storage_cost")] ProtocolFeature::LowerStorageCost => 42, ProtocolFeature::DeleteActionRestriction => 43, + ProtocolFeature::FixApplyChunks => 44, // Nightly features #[cfg(feature = "protocol_feature_forward_chunk_parts")] diff --git a/neard/res/genesis_config.json b/neard/res/genesis_config.json index 9c9168711ef..0c35e8daaf8 100644 --- a/neard/res/genesis_config.json +++ b/neard/res/genesis_config.json @@ -1,239 +1,239 @@ { - "protocol_version": 43, + "protocol_version": 44, "genesis_time": "1970-01-01T00:00:00.000000000Z", "chain_id": "sample", "genesis_height": 0, "num_block_producer_seats": 50, "num_block_producer_seats_per_shard": [ 50 - ], + ], "avg_hidden_validator_seats_per_shard": [ 0 - ], - "dynamic_resharding": false, + ], + "dynamic_resharding": false, "protocol_upgrade_stake_threshold": [ - 4, + 4, 5 - ], - "protocol_upgrade_num_epochs": 2, - "epoch_length": 500, - "gas_limit": 1000000000000000, - "min_gas_price": "1000000000", - "max_gas_price": "10000000000000000000000", - "block_producer_kickout_threshold": 90, - "chunk_producer_kickout_threshold": 90, + ], + "protocol_upgrade_num_epochs": 2, + "epoch_length": 500, + "gas_limit": 1000000000000000, + "min_gas_price": "1000000000", + "max_gas_price": "10000000000000000000000", + "block_producer_kickout_threshold": 90, + "chunk_producer_kickout_threshold": 90, "online_min_threshold": [ - 9, + 9, 10 - ], + ], "online_max_threshold": [ - 99, + 99, 100 - ], + ], "gas_price_adjustment_rate": [ - 1, + 1, 100 - ], + ], "runtime_config": { - "storage_amount_per_byte": "90900000000000000000", + "storage_amount_per_byte": "90900000000000000000", "transaction_costs": { "action_receipt_creation_config": { - "send_sir": 108059500000, - "send_not_sir": 108059500000, + "send_sir": 108059500000, + "send_not_sir": 108059500000, "execution": 108059500000 - }, + }, "data_receipt_creation_config": { "base_cost": { - "send_sir": 4697339419375, - "send_not_sir": 4697339419375, + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, "execution": 4697339419375 - }, + }, "cost_per_byte": { - "send_sir": 59357464, - "send_not_sir": 59357464, + "send_sir": 59357464, + "send_not_sir": 59357464, "execution": 59357464 } - }, + }, "action_creation_config": { "create_account_cost": { - "send_sir": 99607375000, - "send_not_sir": 99607375000, + "send_sir": 99607375000, + "send_not_sir": 99607375000, "execution": 99607375000 - }, + }, "deploy_contract_cost": { - "send_sir": 184765750000, - "send_not_sir": 184765750000, + "send_sir": 184765750000, + "send_not_sir": 184765750000, "execution": 184765750000 - }, + }, "deploy_contract_cost_per_byte": { - "send_sir": 6812999, - "send_not_sir": 6812999, + "send_sir": 6812999, + "send_not_sir": 6812999, "execution": 6812999 - }, + }, "function_call_cost": { - "send_sir": 2319861500000, - "send_not_sir": 2319861500000, + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, "execution": 2319861500000 - }, + }, "function_call_cost_per_byte": { - "send_sir": 2235934, - "send_not_sir": 2235934, + "send_sir": 2235934, + "send_not_sir": 2235934, "execution": 2235934 - }, + }, "transfer_cost": { - "send_sir": 115123062500, - "send_not_sir": 115123062500, + "send_sir": 115123062500, + "send_not_sir": 115123062500, "execution": 115123062500 - }, + }, "stake_cost": { - "send_sir": 141715687500, - "send_not_sir": 141715687500, + "send_sir": 141715687500, + "send_not_sir": 141715687500, "execution": 102217625000 - }, + }, "add_key_cost": { "full_access_cost": { - "send_sir": 101765125000, - "send_not_sir": 101765125000, + "send_sir": 101765125000, + "send_not_sir": 101765125000, "execution": 101765125000 - }, + }, "function_call_cost": { - "send_sir": 102217625000, - "send_not_sir": 102217625000, + "send_sir": 102217625000, + "send_not_sir": 102217625000, "execution": 102217625000 - }, + }, "function_call_cost_per_byte": { - "send_sir": 1925331, - "send_not_sir": 1925331, + "send_sir": 1925331, + "send_not_sir": 1925331, "execution": 1925331 } - }, + }, "delete_key_cost": { - "send_sir": 94946625000, - "send_not_sir": 94946625000, + "send_sir": 94946625000, + "send_not_sir": 94946625000, "execution": 94946625000 - }, + }, "delete_account_cost": { - "send_sir": 147489000000, - "send_not_sir": 147489000000, + "send_sir": 147489000000, + "send_not_sir": 147489000000, "execution": 147489000000 } - }, + }, "storage_usage_config": { - "num_bytes_account": 100, + "num_bytes_account": 100, "num_extra_bytes_record": 40 - }, + }, "burnt_gas_reward": [ - 3, + 3, 10 - ], + ], "pessimistic_gas_price_inflation_ratio": [ - 103, + 103, 100 ] - }, + }, "wasm_config": { "ext_costs": { - "base": 264768111, - "contract_compile_base": 35445963, - "contract_compile_bytes": 216750, - "read_memory_base": 2609863200, - "read_memory_byte": 3801333, - "write_memory_base": 2803794861, - "write_memory_byte": 2723772, - "read_register_base": 2517165186, - "read_register_byte": 98562, - "write_register_base": 2865522486, - "write_register_byte": 3801564, - "utf8_decoding_base": 3111779061, - "utf8_decoding_byte": 291580479, - "utf16_decoding_base": 3543313050, - "utf16_decoding_byte": 163577493, - "sha256_base": 4540970250, - "sha256_byte": 24117351, - "keccak256_base": 5879491275, - "keccak256_byte": 21471105, - "keccak512_base": 5811388236, - "keccak512_byte": 36649701, - "log_base": 3543313050, - "log_byte": 13198791, - "storage_write_base": 64196736000, - "storage_write_key_byte": 70482867, - "storage_write_value_byte": 31018539, - "storage_write_evicted_byte": 32117307, - "storage_read_base": 56356845750, - "storage_read_key_byte": 30952533, - "storage_read_value_byte": 5611005, - "storage_remove_base": 53473030500, - "storage_remove_key_byte": 38220384, - "storage_remove_ret_value_byte": 11531556, - "storage_has_key_base": 54039896625, - "storage_has_key_byte": 30790845, - "storage_iter_create_prefix_base": 0, - "storage_iter_create_prefix_byte": 0, - "storage_iter_create_range_base": 0, - "storage_iter_create_from_byte": 0, - "storage_iter_create_to_byte": 0, - "storage_iter_next_base": 0, - "storage_iter_next_key_byte": 0, - "storage_iter_next_value_byte": 0, - "touching_trie_node": 16101955926, - "promise_and_base": 1465013400, - "promise_and_per_promise": 5452176, - "promise_return": 560152386, - "validator_stake_base": 911834726400, + "base": 264768111, + "contract_compile_base": 35445963, + "contract_compile_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, "validator_total_stake_base": 911834726400 - }, - "grow_mem_cost": 1, - "regular_op_cost": 3856371, + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, "limit_config": { - "max_gas_burnt": 200000000000000, - "max_gas_burnt_view": 200000000000000, - "max_stack_height": 16384, - "initial_memory_pages": 1024, - "max_memory_pages": 2048, - "registers_memory_limit": 1073741824, - "max_register_size": 104857600, - "max_number_registers": 100, - "max_number_logs": 100, - "max_total_log_length": 16384, - "max_total_prepaid_gas": 300000000000000, - "max_actions_per_receipt": 100, - "max_number_bytes_method_names": 2000, - "max_length_method_name": 256, - "max_arguments_length": 4194304, - "max_length_returned_data": 4194304, - "max_contract_size": 4194304, - "max_length_storage_key": 4194304, - "max_length_storage_value": 4194304, - "max_promises_per_function_call_action": 1024, + "max_gas_burnt": 200000000000000, + "max_gas_burnt_view": 200000000000000, + "max_stack_height": 16384, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, "max_number_input_data_dependencies": 128 } - }, + }, "account_creation_config": { - "min_allowed_top_level_account_length": 0, + "min_allowed_top_level_account_length": 0, "registrar_account_id": "registrar" } - }, + }, "validators": [ { - "account_id": "test.near", - "public_key": "ed25519:9BmAFNRTa5mRRXpSAm6MxSEeqRASDGNh2FuuwZ4gyxTw", + "account_id": "test.near", + "public_key": "ed25519:9BmAFNRTa5mRRXpSAm6MxSEeqRASDGNh2FuuwZ4gyxTw", "amount": "50000000000000000000000000000000" } - ], - "transaction_validity_period": 100, + ], + "transaction_validity_period": 100, "protocol_reward_rate": [ - 1, + 1, 10 - ], + ], "max_inflation_rate": [ - 1, + 1, 20 - ], - "total_supply": "2050000000000000000000000000000000", - "num_blocks_per_year": 31536000, - "protocol_treasury_account": "test.near", - "fishermen_threshold": "10000000000000000000000000", - "minimum_stake_divisor": 10, + ], + "total_supply": "2050000000000000000000000000000000", + "num_blocks_per_year": 31536000, + "protocol_treasury_account": "test.near", + "fishermen_threshold": "10000000000000000000000000", + "minimum_stake_divisor": 10, "records": [] } diff --git a/neard/src/lib.rs b/neard/src/lib.rs index e61512cee47..603e8d9fdce 100644 --- a/neard/src/lib.rs +++ b/neard/src/lib.rs @@ -20,7 +20,7 @@ use near_store::{create_store, Store}; use near_telemetry::TelemetryActor; pub use crate::config::{init_configs, load_config, load_test_config, NearConfig, NEAR_BASE}; -use crate::migrations::{migrate_12_to_13, migrate_18_to_19}; +use crate::migrations::{migrate_12_to_13, migrate_18_to_19, migrate_19_to_20}; pub use crate::runtime::NightshadeRuntime; use near_store::migrations::{ fill_col_outcomes_by_hash, fill_col_transaction_refcount, get_store_version, migrate_10_to_11, @@ -198,6 +198,11 @@ pub fn apply_store_migrations(path: &String, near_config: &NearConfig) { // version 18 => 19: populate ColEpochValidatorInfo for archival nodes migrate_18_to_19(&path, near_config); } + if db_version <= 19 { + info!(target: "near", "Migrate DB from version 19 to 20"); + // version 19 => 20: fix execution outcome + migrate_19_to_20(&path, &near_config); + } #[cfg(feature = "protocol_feature_rectify_inflation")] if db_version <= 18 { // version 18 => rectify inflation: add `timestamp` to `BlockInfo` diff --git a/neard/src/migrations.rs b/neard/src/migrations.rs index 892d5d04ab4..fcde82b3b3c 100644 --- a/neard/src/migrations.rs +++ b/neard/src/migrations.rs @@ -62,6 +62,7 @@ fn apply_block_at_height( chunk_header.gas_limit(), &block.header().challenges_result(), *block.header().random_value(), + true, ) .unwrap(); let (_, outcome_paths) = ApplyTransactionResult::compute_outcomes_proof(&apply_result.outcomes); @@ -123,6 +124,7 @@ pub fn migrate_12_to_13(path: &String, near_config: &NearConfig) { } pub fn migrate_18_to_19(path: &String, near_config: &NearConfig) { + use near_primitives::types::EpochId; let store = create_store(path); if near_config.client_config.archive { let genesis_height = near_config.genesis.config.genesis_height; @@ -135,24 +137,123 @@ pub fn migrate_18_to_19(path: &String, near_config: &NearConfig) { near_config.genesis.config.validators(), ) .unwrap(); - for (_, value) in store.iter(DBCol::ColEpochStart) { + for (key, value) in store.iter(DBCol::ColEpochStart) { + let epoch_id = EpochId::try_from_slice(&key).unwrap(); let epoch_start_height = u64::try_from_slice(&value).unwrap(); - let block_hash = chain_store.get_block_hash_by_height(epoch_start_height).unwrap(); + // This is a temporary workaround due to https://github.com/near/nearcore/issues/4243 + let mut counter = 0; + let mut check_height = |height: u64| -> bool { + if let Ok(block_hash) = chain_store.get_block_hash_by_height(height) { + let block_header = chain_store.get_block_header(&block_hash).unwrap().clone(); + let prev_block_epoch_id = { + if let Ok(block_header) = chain_store.get_previous_header(&block_header) { + block_header.epoch_id().clone() + } else { + EpochId::default() + } + }; + if block_header.epoch_id() == &epoch_id + && (prev_block_epoch_id != epoch_id || epoch_id == EpochId::default()) + { + return true; + } + } + false + }; + let real_epoch_start_height = loop { + let height1 = epoch_start_height + counter; + let height2 = epoch_start_height - counter; + if check_height(height1) { + break height1; + } + if check_height(height2) { + break height2; + } + counter += 1; + }; + let block_hash = chain_store.get_block_hash_by_height(real_epoch_start_height).unwrap(); let block_header = chain_store.get_block_header(&block_hash).unwrap().clone(); - let prev_header = chain_store.get_previous_header(&block_header).unwrap().clone(); - let last_finalized_height = chain_store - .get_block_header(prev_header.last_final_block()) - .map(|h| h.height()) - .unwrap_or(genesis_height); - let mut store_update = store.store_update(); - epoch_manager - .migrate_18_to_19( - &BlockHeaderInfo::new(&prev_header, last_finalized_height), - &mut store_update, - ) - .unwrap(); - store_update.commit().unwrap(); + if let Ok(prev_header) = + chain_store.get_previous_header(&block_header).map(Clone::clone) + { + let last_finalized_height = chain_store + .get_block_header(prev_header.last_final_block()) + .map(|h| h.height()) + .unwrap_or(genesis_height); + let mut store_update = store.store_update(); + epoch_manager + .migrate_18_to_19( + &BlockHeaderInfo::new(&prev_header, last_finalized_height), + &mut store_update, + ) + .unwrap(); + store_update.commit().unwrap(); + } } } set_store_version(&store, 19); } + +pub fn migrate_19_to_20(path: &String, near_config: &NearConfig) { + let store = create_store(path); + if near_config.client_config.archive { + let genesis_height = near_config.genesis.config.genesis_height; + let mut chain_store = ChainStore::new(store.clone(), genesis_height); + let head = chain_store.head().unwrap(); + let runtime = NightshadeRuntime::new( + &Path::new(path), + store.clone(), + &near_config.genesis, + near_config.client_config.tracked_accounts.clone(), + near_config.client_config.tracked_shards.clone(), + ); + let shard_id = 0; + // This is hardcoded for mainnet specifically. Blocks with lower heights have been checked. + let start_height = 34691244; + for block_height in start_height..=head.height { + if let Ok(block_hash) = chain_store.get_block_hash_by_height(block_height) { + let block = chain_store.get_block(&block_hash).unwrap().clone(); + if block.chunks()[shard_id as usize].height_included() != block.header().height() { + let mut chain_store_update = ChainStoreUpdate::new(&mut chain_store); + let new_extra = chain_store_update + .get_chunk_extra(block.header().prev_hash(), shard_id) + .unwrap() + .clone(); + + let apply_result = runtime + .apply_transactions( + shard_id, + new_extra.state_root(), + block.header().height(), + block.header().raw_timestamp(), + block.header().prev_hash(), + &block.hash(), + &[], + &[], + new_extra.validator_proposals(), + block.header().gas_price(), + new_extra.gas_limit(), + &block.header().challenges_result(), + *block.header().random_value(), + // doesn't really matter here since the old blocks are on the old version + false, + ) + .unwrap(); + if !apply_result.outcomes.is_empty() { + let (_, outcome_paths) = + ApplyTransactionResult::compute_outcomes_proof(&apply_result.outcomes); + chain_store_update.save_outcomes_with_proofs( + &block.hash(), + shard_id, + apply_result.outcomes, + outcome_paths, + ); + chain_store_update.commit().unwrap(); + } + } + } + } + } + + set_store_version(&store, 20); +} diff --git a/neard/src/runtime/mod.rs b/neard/src/runtime/mod.rs index 539e70c164d..cae7ca5cca2 100644 --- a/neard/src/runtime/mod.rs +++ b/neard/src/runtime/mod.rs @@ -327,6 +327,7 @@ impl NightshadeRuntime { gas_limit: Gas, challenges_result: &ChallengesResult, random_seed: CryptoHash, + is_new_chunk: bool, ) -> Result { let validator_accounts_update = { let mut epoch_manager = self.epoch_manager.as_ref().write().expect(POISONED_LOCK_ERR); @@ -414,6 +415,7 @@ impl NightshadeRuntime { current_protocol_version, ), cache: Some(Arc::new(StoreCompiledContractCache { store: self.store.clone() })), + is_new_chunk, #[cfg(feature = "protocol_feature_evm")] evm_chain_id: self.evm_chain_id(), profile: Default::default(), @@ -1133,6 +1135,7 @@ impl RuntimeAdapter for NightshadeRuntime { challenges: &ChallengesResult, random_seed: CryptoHash, generate_storage_proof: bool, + is_new_chunk: bool, ) -> Result { let trie = self.get_trie_for_shard(shard_id); let trie = if generate_storage_proof { trie.recording_reads() } else { trie }; @@ -1151,6 +1154,7 @@ impl RuntimeAdapter for NightshadeRuntime { gas_limit, challenges, random_seed, + is_new_chunk, ) { Ok(result) => Ok(result), Err(e) => match e.kind() { @@ -1178,6 +1182,7 @@ impl RuntimeAdapter for NightshadeRuntime { gas_limit: Gas, challenges: &ChallengesResult, random_value: CryptoHash, + is_new_chunk: bool, ) -> Result { let trie = Trie::from_recorded_storage(partial_storage); self.process_state_update( @@ -1195,6 +1200,7 @@ impl RuntimeAdapter for NightshadeRuntime { gas_limit, challenges, random_value, + is_new_chunk, ) } @@ -1669,6 +1675,7 @@ mod test { gas_limit, challenges, CryptoHash::default(), + true, ) .unwrap(); let mut store_update = self.store.store_update(); diff --git a/runtime/runtime-params-estimator/src/testbed.rs b/runtime/runtime-params-estimator/src/testbed.rs index e04b3336f32..087423c511d 100644 --- a/runtime/runtime-params-estimator/src/testbed.rs +++ b/runtime/runtime-params-estimator/src/testbed.rs @@ -91,6 +91,7 @@ impl RuntimeTestbed { current_protocol_version: PROTOCOL_VERSION, config: Arc::new(runtime_config), cache: Some(Arc::new(StoreCompiledContractCache { store: tries.get_store() })), + is_new_chunk: true, #[cfg(feature = "protocol_feature_evm")] evm_chain_id: near_chain_configs::TESTNET_EVM_CHAIN_ID, profile: Default::default(), diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 1efa3bb75da..c1b51b4627f 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -49,8 +49,11 @@ use crate::config::{ }; use crate::verifier::validate_receipt; pub use crate::verifier::{validate_transaction, verify_and_charge_transaction}; +pub use near_primitives::runtime::apply_state::ApplyState; use near_primitives::runtime::fees::RuntimeFeesConfig; -use near_primitives::version::{is_implicit_account_creation_enabled, ProtocolVersion}; +use near_primitives::version::{ + is_implicit_account_creation_enabled, ProtocolFeature, ProtocolVersion, +}; use std::borrow::Borrow; use std::rc::Rc; @@ -65,7 +68,6 @@ pub mod state_viewer; mod verifier; const EXPECT_ACCOUNT_EXISTS: &str = "account exists, checked above"; -pub use near_primitives::runtime::apply_state::ApplyState; /// Contains information to update validators accounts at the first block of a new epoch. #[derive(Debug)] @@ -1100,6 +1102,23 @@ impl Runtime { &mut stats, )?; } + if !apply_state.is_new_chunk + && apply_state.current_protocol_version + >= ProtocolFeature::FixApplyChunks.protocol_version() + { + let (trie_changes, state_changes) = state_update.finalize()?; + let proof = trie.recorded_storage(); + return Ok(ApplyResult { + state_root: trie_changes.new_root, + trie_changes, + validator_proposals: vec![], + outgoing_receipts: vec![], + outcomes: vec![], + state_changes, + stats, + proof, + }); + } let mut outgoing_receipts = Vec::new(); let mut validator_proposals = vec![]; @@ -1534,6 +1553,7 @@ mod tests { current_protocol_version: PROTOCOL_VERSION, config: Arc::new(RuntimeConfig::default()), cache: Some(Arc::new(StoreCompiledContractCache { store: tries.get_store() })), + is_new_chunk: true, #[cfg(feature = "protocol_feature_evm")] evm_chain_id: near_chain_configs::TESTNET_EVM_CHAIN_ID, profile: ProfileData::new_enabled(), diff --git a/runtime/runtime/src/state_viewer/mod.rs b/runtime/runtime/src/state_viewer/mod.rs index 1f67ebfe01f..d45ab4499f7 100644 --- a/runtime/runtime/src/state_viewer/mod.rs +++ b/runtime/runtime/src/state_viewer/mod.rs @@ -219,6 +219,7 @@ impl TrieViewer { current_protocol_version: view_state.current_protocol_version, config: config.clone(), cache: view_state.cache, + is_new_chunk: false, #[cfg(feature = "protocol_feature_evm")] evm_chain_id: view_state.evm_chain_id, profile: Default::default(), diff --git a/runtime/runtime/tests/runtime_group_tools/mod.rs b/runtime/runtime/tests/runtime_group_tools/mod.rs index 352d0d6b1a5..48f3532250c 100644 --- a/runtime/runtime/tests/runtime_group_tools/mod.rs +++ b/runtime/runtime/tests/runtime_group_tools/mod.rs @@ -69,6 +69,7 @@ impl StandaloneRuntime { current_protocol_version: PROTOCOL_VERSION, config: Arc::new(runtime_config), cache: None, + is_new_chunk: true, #[cfg(feature = "protocol_feature_evm")] evm_chain_id: near_chain_configs::TESTNET_EVM_CHAIN_ID, profile: Default::default(), diff --git a/test-utils/state-viewer/src/main.rs b/test-utils/state-viewer/src/main.rs index 6855a161201..f05b0e187a8 100644 --- a/test-utils/state-viewer/src/main.rs +++ b/test-utils/state-viewer/src/main.rs @@ -220,45 +220,69 @@ fn apply_block_at_height( ); let block_hash = chain_store.get_block_hash_by_height(height).unwrap(); let block = chain_store.get_block(&block_hash).unwrap().clone(); - assert_eq!(block.chunks()[shard_id as usize].height_included(), height); - let chunk = - chain_store.get_chunk(&block.chunks()[shard_id as usize].chunk_hash()).unwrap().clone(); - let prev_block = chain_store.get_block(&block.header().prev_hash()).unwrap().clone(); - let mut chain_store_update = ChainStoreUpdate::new(&mut chain_store); - let receipt_proof_response = chain_store_update - .get_incoming_receipts_for_shard( - shard_id, - block_hash, - prev_block.chunks()[shard_id as usize].height_included(), - ) - .unwrap(); - let receipts = collect_receipts_from_response(&receipt_proof_response); + let apply_result = if block.chunks()[shard_id as usize].height_included() == height { + let chunk = + chain_store.get_chunk(&block.chunks()[shard_id as usize].chunk_hash()).unwrap().clone(); + let prev_block = chain_store.get_block(&block.header().prev_hash()).unwrap().clone(); + let mut chain_store_update = ChainStoreUpdate::new(&mut chain_store); + let receipt_proof_response = chain_store_update + .get_incoming_receipts_for_shard( + shard_id, + block_hash, + prev_block.chunks()[shard_id as usize].height_included(), + ) + .unwrap(); + let receipts = collect_receipts_from_response(&receipt_proof_response); - let chunk_inner = chunk.cloned_header().take_inner(); - let apply_result = runtime - .apply_transactions( - shard_id, - chunk_inner.prev_state_root(), - height, - block.header().raw_timestamp(), - block.header().prev_hash(), - block.hash(), - &receipts, - chunk.transactions(), - chunk_inner.validator_proposals(), - prev_block.header().gas_price(), - chunk_inner.gas_limit(), - &block.header().challenges_result(), - *block.header().random_value(), - ) - .unwrap(); + let chunk_inner = chunk.cloned_header().take_inner(); + runtime + .apply_transactions( + shard_id, + chunk_inner.prev_state_root(), + height, + block.header().raw_timestamp(), + block.header().prev_hash(), + block.hash(), + &receipts, + chunk.transactions(), + chunk_inner.validator_proposals(), + prev_block.header().gas_price(), + chunk_inner.gas_limit(), + &block.header().challenges_result(), + *block.header().random_value(), + true, + ) + .unwrap() + } else { + let chunk_extra = + chain_store.get_chunk_extra(block.header().prev_hash(), shard_id).unwrap().clone(); + + runtime + .apply_transactions( + shard_id, + chunk_extra.state_root(), + block.header().height(), + block.header().raw_timestamp(), + block.header().prev_hash(), + &block.hash(), + &[], + &[], + chunk_extra.validator_proposals(), + block.header().gas_price(), + chunk_extra.gas_limit(), + &block.header().challenges_result(), + *block.header().random_value(), + false, + ) + .unwrap() + }; let (outcome_root, _) = ApplyTransactionResult::compute_outcomes_proof(&apply_result.outcomes); let chunk_extra = ChunkExtra::new( &apply_result.new_root, outcome_root, apply_result.validator_proposals, apply_result.total_gas_burnt, - chunk_inner.gas_limit(), + near_config.genesis.config.gas_limit, apply_result.total_balance_burnt, ); diff --git a/test-utils/testlib/src/user/runtime_user.rs b/test-utils/testlib/src/user/runtime_user.rs index 4df1e6c64cd..d36901b1d33 100644 --- a/test-utils/testlib/src/user/runtime_user.rs +++ b/test-utils/testlib/src/user/runtime_user.rs @@ -139,6 +139,7 @@ impl RuntimeUser { current_protocol_version: PROTOCOL_VERSION, config: self.runtime_config.clone(), cache: None, + is_new_chunk: true, #[cfg(feature = "protocol_feature_evm")] evm_chain_id: TESTNET_EVM_CHAIN_ID, profile: Default::default(),