diff --git a/chain/chain/src/chain_update.rs b/chain/chain/src/chain_update.rs index d1fd7bbf170..6ca98e3edda 100644 --- a/chain/chain/src/chain_update.rs +++ b/chain/chain/src/chain_update.rs @@ -155,6 +155,7 @@ impl<'a> ChainUpdate<'a> { shard_id, apply_result.proof, apply_result.applied_receipts_hash, + apply_result.contract_accesses, ); } } @@ -184,6 +185,7 @@ impl<'a> ChainUpdate<'a> { shard_uid.shard_id(), apply_result.proof, apply_result.applied_receipts_hash, + apply_result.contract_accesses, ); } } diff --git a/chain/chain/src/resharding/manager.rs b/chain/chain/src/resharding/manager.rs index fd0a0bd9c4e..edccd82d88a 100644 --- a/chain/chain/src/resharding/manager.rs +++ b/chain/chain/src/resharding/manager.rs @@ -159,6 +159,9 @@ impl ReshardingManager { new_shard_uid.shard_id(), Some(partial_storage), CryptoHash::default(), + // No contract code is accessed during resharding. + // TODO(#11099): Confirm if sending no contracts is ok here. + vec![], ); // Commit `TrieChanges` directly. They are needed to serve reads of diff --git a/chain/chain/src/runtime/mod.rs b/chain/chain/src/runtime/mod.rs index adec8fe5870..5b29b85d3f8 100644 --- a/chain/chain/src/runtime/mod.rs +++ b/chain/chain/src/runtime/mod.rs @@ -466,6 +466,7 @@ impl NightshadeRuntime { processed_yield_timeouts: apply_result.processed_yield_timeouts, applied_receipts_hash: hash(&borsh::to_vec(receipts).unwrap()), congestion_info: apply_result.congestion_info, + contract_accesses: apply_result.contract_accesses, }; Ok(result) diff --git a/chain/chain/src/stateless_validation/state_transition_data.rs b/chain/chain/src/stateless_validation/state_transition_data.rs index f57d0f35c47..798e353bd45 100644 --- a/chain/chain/src/stateless_validation/state_transition_data.rs +++ b/chain/chain/src/stateless_validation/state_transition_data.rs @@ -115,7 +115,9 @@ mod tests { use near_primitives::block_header::{BlockHeader, BlockHeaderInnerLite, BlockHeaderV4}; use near_primitives::hash::{hash, CryptoHash}; - use near_primitives::stateless_validation::stored_chunk_state_transition_data::StoredChunkStateTransitionData; + use near_primitives::stateless_validation::stored_chunk_state_transition_data::{ + StoredChunkStateTransitionData, StoredChunkStateTransitionDataV1, + }; use near_primitives::types::{new_shard_id_tmp, BlockHeight, EpochId, ShardId}; use near_primitives::utils::{get_block_shard_id, get_block_shard_id_rev, index_to_bytes}; use near_store::db::STATE_TRANSITION_START_HEIGHTS; @@ -202,10 +204,12 @@ mod tests { .set_ser( DBCol::StateTransitionData, &get_block_shard_id(&block_hash, shard_id), - &StoredChunkStateTransitionData { + &StoredChunkStateTransitionData::V1(StoredChunkStateTransitionDataV1 { base_state: Default::default(), receipts_hash: Default::default(), - }, + // TODO(#11099): Revisit this. + contract_accesses: vec![], + }), ) .unwrap(); store_update diff --git a/chain/chain/src/store/mod.rs b/chain/chain/src/store/mod.rs index 34c10ff28c8..68286d07cb0 100644 --- a/chain/chain/src/store/mod.rs +++ b/chain/chain/src/store/mod.rs @@ -23,7 +23,10 @@ use near_primitives::sharding::{ use near_primitives::state_sync::{ ReceiptProofResponse, ShardStateSyncResponseHeader, StateHeaderKey, StateSyncDumpProgress, }; -use near_primitives::stateless_validation::stored_chunk_state_transition_data::StoredChunkStateTransitionData; +use near_primitives::stateless_validation::contract_distribution::CodeHash; +use near_primitives::stateless_validation::stored_chunk_state_transition_data::{ + StoredChunkStateTransitionData, StoredChunkStateTransitionDataV1, +}; use near_primitives::transaction::{ ExecutionOutcomeWithId, ExecutionOutcomeWithIdAndProof, ExecutionOutcomeWithProof, SignedTransaction, @@ -2013,14 +2016,16 @@ impl<'a> ChainStoreUpdate<'a> { shard_id: ShardId, partial_storage: Option, applied_receipts_hash: CryptoHash, + contract_accesses: Vec, ) { if let Some(partial_storage) = partial_storage { self.state_transition_data.insert( (block_hash, shard_id), - StoredChunkStateTransitionData { + StoredChunkStateTransitionData::V1(StoredChunkStateTransitionDataV1 { base_state: partial_storage.nodes, receipts_hash: applied_receipts_hash, - }, + contract_accesses: contract_accesses, + }), ); } } diff --git a/chain/chain/src/test_utils/kv_runtime.rs b/chain/chain/src/test_utils/kv_runtime.rs index 3f4fff06a19..409a0c6b773 100644 --- a/chain/chain/src/test_utils/kv_runtime.rs +++ b/chain/chain/src/test_utils/kv_runtime.rs @@ -1343,6 +1343,8 @@ impl RuntimeAdapter for KeyValueRuntime { processed_yield_timeouts: vec![], applied_receipts_hash: hash(&borsh::to_vec(receipts).unwrap()), congestion_info: Self::get_congestion_info(PROTOCOL_VERSION), + // Since all actions are transfer actions, there is no contracts accessed. + contract_accesses: vec![], }) } diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index 1063ec140d2..7467be915c4 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -22,6 +22,7 @@ use near_primitives::receipt::{PromiseYieldTimeout, Receipt}; use near_primitives::sandbox::state_patch::SandboxStatePatch; use near_primitives::shard_layout::ShardUId; use near_primitives::state_part::PartId; +use near_primitives::stateless_validation::contract_distribution::CodeHash; use near_primitives::transaction::{ExecutionOutcomeWithId, SignedTransaction}; use near_primitives::types::validator_stake::{ValidatorStake, ValidatorStakeIter}; use near_primitives::types::{ @@ -101,6 +102,8 @@ pub struct ApplyChunkResult { /// should be set to None for chunks before the CongestionControl protocol /// version and Some otherwise. pub congestion_info: Option, + /// Hashes of the contracts accessed while applying the chunk. + pub contract_accesses: Vec, } impl ApplyChunkResult { diff --git a/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs b/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs index a9689542dee..c3eae08fbd6 100644 --- a/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs +++ b/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs @@ -8,12 +8,16 @@ use near_chain::Error; use near_chain_configs::MutableValidatorSigner; use near_epoch_manager::EpochManagerAdapter; use near_network::state_witness::{ - ChunkStateWitnessAckMessage, PartialEncodedStateWitnessForwardMessage, + ChunkContractAccessesMessage, ChunkStateWitnessAckMessage, ContractCodeRequestMessage, + ContractCodeResponseMessage, PartialEncodedStateWitnessForwardMessage, PartialEncodedStateWitnessMessage, }; use near_network::types::{NetworkRequests, PeerManagerAdapter, PeerManagerMessageRequest}; use near_performance_metrics_macros::perf; use near_primitives::sharding::ShardChunkHeader; +use near_primitives::stateless_validation::contract_distribution::{ + ChunkContractAccesses, ContractCodeRequest, ContractCodeResponse, +}; use near_primitives::stateless_validation::partial_witness::PartialEncodedStateWitness; use near_primitives::stateless_validation::state_witness::{ ChunkStateWitness, ChunkStateWitnessAck, EncodedChunkStateWitness, @@ -99,6 +103,30 @@ impl Handler for PartialWitnessActor { } } +impl Handler for PartialWitnessActor { + fn handle(&mut self, msg: ChunkContractAccessesMessage) { + if let Err(err) = self.handle_chunk_contract_accesses(msg.0) { + tracing::error!(target: "client", ?err, "Failed to handle ChunkContractAccessesMessage"); + } + } +} + +impl Handler for PartialWitnessActor { + fn handle(&mut self, msg: ContractCodeRequestMessage) { + if let Err(err) = self.handle_contract_code_request(msg.0) { + tracing::error!(target: "client", ?err, "Failed to handle ContractCodeRequestMessage"); + } + } +} + +impl Handler for PartialWitnessActor { + fn handle(&mut self, msg: ContractCodeResponseMessage) { + if let Err(err) = self.handle_contract_code_response(msg.0) { + tracing::error!(target: "client", ?err, "Failed to handle ContractCodeResponseMessage"); + } + } +} + impl PartialWitnessActor { pub fn new( clock: Clock, @@ -319,6 +347,40 @@ impl PartialWitnessActor { pub fn handle_chunk_state_witness_ack(&mut self, witness_ack: ChunkStateWitnessAck) { self.state_witness_tracker.on_witness_ack_received(witness_ack); } + + /// Handles contract code accesses message from chunk producer. + /// This is sent in parallel to a chunk state witness and contains the code-hashes + /// of the contracts accessed when applying the previous chunk of the witness. + pub fn handle_chunk_contract_accesses( + &mut self, + _accesses: ChunkContractAccesses, + ) -> Result<(), Error> { + // TODO(#11099): Implement this and remove debug message. + tracing::debug!(target: "client", next_chunk=?_accesses.chunk_production_key(), contracts=?_accesses.contracts(), "handle_chunk_contract_accesses"); + unimplemented!() + } + + /// Handles contract code requests message from chunk validators. + /// As response to this message, sends the contract code requested to + /// the requesting chunk validator for the given code hashes. + pub fn handle_contract_code_request( + &mut self, + _request: ContractCodeRequest, + ) -> Result<(), Error> { + // TODO(#11099): Implement this and remove debug message. + tracing::debug!(target: "client", next_chunk=?_request.chunk_production_key(), contracts=?_request.contracts(), "handle_contract_code_request"); + unimplemented!() + } + + /// Handles contract code responses message from chunk producer. + pub fn handle_contract_code_response( + &mut self, + _response: ContractCodeResponse, + ) -> Result<(), Error> { + // TODO(#11099): Implement this and remove debug message. + tracing::debug!(target: "client", "handle_contract_code_response"); + unimplemented!() + } } fn compress_witness(witness: &ChunkStateWitness) -> Result { diff --git a/chain/client/src/stateless_validation/shadow_validate.rs b/chain/client/src/stateless_validation/shadow_validate.rs index 33df5655de8..8551c093a62 100644 --- a/chain/client/src/stateless_validation/shadow_validate.rs +++ b/chain/client/src/stateless_validation/shadow_validate.rs @@ -74,7 +74,7 @@ impl Client { )); }; - let witness = self.create_state_witness( + let (witness, _contract_accesses) = self.create_state_witness( // Setting arbitrary chunk producer is OK for shadow validation "alice.near".parse().unwrap(), prev_block_header, diff --git a/chain/client/src/stateless_validation/state_witness_producer.rs b/chain/client/src/stateless_validation/state_witness_producer.rs index 92e744eecd7..f0a43ba5c79 100644 --- a/chain/client/src/stateless_validation/state_witness_producer.rs +++ b/chain/client/src/stateless_validation/state_witness_producer.rs @@ -4,24 +4,41 @@ use std::sync::Arc; use near_async::messaging::{CanSend, IntoSender}; use near_chain::{BlockHeader, Chain, ChainStoreAccess}; use near_chain_primitives::Error; +use near_network::types::{NetworkRequests, PeerManagerMessageRequest}; use near_o11y::log_assert_fail; use near_primitives::challenge::PartialState; use near_primitives::checked_feature; use near_primitives::hash::{hash, CryptoHash}; use near_primitives::receipt::Receipt; use near_primitives::sharding::{ChunkHash, ReceiptProof, ShardChunk, ShardChunkHeader}; +use near_primitives::stateless_validation::contract_distribution::{ + ChunkContractAccesses, CodeHash, +}; use near_primitives::stateless_validation::state_witness::{ ChunkStateTransition, ChunkStateWitness, }; -use near_primitives::stateless_validation::stored_chunk_state_transition_data::StoredChunkStateTransitionData; +use near_primitives::stateless_validation::stored_chunk_state_transition_data::{ + StoredChunkStateTransitionData, StoredChunkStateTransitionDataV1, +}; +use near_primitives::stateless_validation::ChunkProductionKey; use near_primitives::types::{AccountId, EpochId}; use near_primitives::validator_signer::ValidatorSigner; +use near_primitives::version::ProtocolFeature; use crate::stateless_validation::chunk_validator::send_chunk_endorsement_to_block_producers; use crate::Client; use super::partial_witness::partial_witness_actor::DistributeStateWitnessRequest; +/// Result of collecting state transition data from the database to generate a state witness. +/// Keep this private to this file. +struct StateTransitionData { + main_transition: ChunkStateTransition, + implicit_transitions: Vec, + applied_receipts_hash: CryptoHash, + contract_accesses: Vec, +} + impl Client { /// Distributes the chunk state witness to chunk validators that are /// selected to validate this chunk. @@ -44,7 +61,7 @@ impl Client { let my_signer = validator_signer.as_ref().ok_or(Error::NotAValidator(format!("send state witness")))?; - let state_witness = self.create_state_witness( + let (state_witness, contract_accesses) = self.create_state_witness( my_signer.validator_id().clone(), prev_block_header, prev_chunk_header, @@ -72,6 +89,17 @@ impl Client { ); } + if ProtocolFeature::ExcludeContractCodeFromStateWitness.enabled(protocol_version) + && !contract_accesses.is_empty() + { + self.send_contract_accesses_to_chunk_validators( + epoch_id, + &chunk_header, + contract_accesses, + my_signer.as_ref(), + ); + } + self.partial_witness_adapter.send(DistributeStateWitnessRequest { epoch_id: *epoch_id, chunk_header, @@ -87,13 +115,17 @@ impl Client { prev_chunk_header: &ShardChunkHeader, chunk: &ShardChunk, transactions_storage_proof: Option, - ) -> Result { + ) -> Result<(ChunkStateWitness, Vec), Error> { let chunk_header = chunk.cloned_header(); let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?; let prev_chunk = self.chain.get_chunk(&prev_chunk_header.chunk_hash())?; - let (main_state_transition, implicit_transitions, applied_receipts_hash) = - self.collect_state_transition_data(&chunk_header, prev_chunk_header)?; + let StateTransitionData { + main_transition, + implicit_transitions, + applied_receipts_hash, + contract_accesses, + } = self.collect_state_transition_data(&chunk_header, prev_chunk_header)?; let new_transactions = chunk.transactions().to_vec(); let new_transactions_validation_state = if new_transactions.is_empty() { @@ -115,7 +147,7 @@ impl Client { chunk_producer, epoch_id, chunk_header, - main_state_transition, + main_transition, source_receipt_proofs, // (Could also be derived from iterating through the receipts, but // that defeats the purpose of this check being a debugging @@ -126,7 +158,7 @@ impl Client { new_transactions, new_transactions_validation_state, ); - Ok(witness) + Ok((witness, contract_accesses)) } /// Collect state transition data necessary to produce state witness for @@ -135,7 +167,7 @@ impl Client { &mut self, chunk_header: &ShardChunkHeader, prev_chunk_header: &ShardChunkHeader, - ) -> Result<(ChunkStateTransition, Vec, CryptoHash), Error> { + ) -> Result { let store = self.chain.chain_store().store(); let shard_id = chunk_header.shard_id(); let epoch_id = @@ -149,10 +181,10 @@ impl Client { )?; prev_blocks.reverse(); let (main_block, implicit_blocks) = prev_blocks.split_first().unwrap(); - let (base_state, receipts_hash) = if prev_chunk_header.is_genesis() { - (Default::default(), hash(&borsh::to_vec::<[Receipt]>(&[]).unwrap())) + let (base_state, receipts_hash, contract_accesses) = if prev_chunk_header.is_genesis() { + (Default::default(), hash(&borsh::to_vec::<[Receipt]>(&[]).unwrap()), vec![]) } else { - let StoredChunkStateTransitionData { base_state, receipts_hash } = store + let state_transition = store .get_ser( near_store::DBCol::StateTransitionData, &near_primitives::utils::get_block_shard_id(main_block, shard_id), @@ -166,7 +198,13 @@ impl Client { } Error::Other(message) })?; - (base_state, receipts_hash) + match state_transition { + StoredChunkStateTransitionData::V1(StoredChunkStateTransitionDataV1 { + base_state, + receipts_hash, + contract_accesses, + }) => (base_state, receipts_hash, contract_accesses), + } }; let main_transition = ChunkStateTransition { block_hash: *main_block, @@ -176,7 +214,7 @@ impl Client { let mut implicit_transitions = vec![]; for block_hash in implicit_blocks { - let StoredChunkStateTransitionData { base_state, .. } = store + let state_transition = store .get_ser( near_store::DBCol::StateTransitionData, &near_primitives::utils::get_block_shard_id(block_hash, shard_id), @@ -190,14 +228,29 @@ impl Client { } Error::Other(message) })?; - implicit_transitions.push(ChunkStateTransition { - block_hash: *block_hash, - base_state, - post_state_root: *self.chain.get_chunk_extra(block_hash, &shard_uid)?.state_root(), - }); + match state_transition { + StoredChunkStateTransitionData::V1(StoredChunkStateTransitionDataV1 { + base_state, + .. + }) => { + implicit_transitions.push(ChunkStateTransition { + block_hash: *block_hash, + base_state, + post_state_root: *self + .chain + .get_chunk_extra(block_hash, &shard_uid)? + .state_root(), + }); + } + } } - Ok((main_transition, implicit_transitions, receipts_hash)) + Ok(StateTransitionData { + main_transition, + implicit_transitions, + applied_receipts_hash: receipts_hash, + contract_accesses, + }) } /// State witness proves the execution of receipts proposed by `prev_chunk`. @@ -283,4 +336,36 @@ impl Client { } Ok(source_receipt_proofs) } + + /// Sends the contract access to the same chunk validators that will receive the state witness for the chunk. + fn send_contract_accesses_to_chunk_validators( + &self, + epoch_id: &EpochId, + chunk_header: &ShardChunkHeader, + contract_accesses: Vec, + my_signer: &ValidatorSigner, + ) { + let chunk_validators = self + .epoch_manager + .get_chunk_validator_assignments( + &epoch_id, + chunk_header.shard_id(), + chunk_header.height_created(), + ) + .expect("Chunk validators must be defined") + .ordered_chunk_validators(); + + let chunk_production_key = ChunkProductionKey { + epoch_id: *epoch_id, + shard_id: chunk_header.shard_id(), + height_created: chunk_header.height_created(), + }; + // TODO(#11099): Optimize the set of receivers by excluding self and chunk producers etc. + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::ChunkContractAccesses( + chunk_validators, + ChunkContractAccesses::new(chunk_production_key, contract_accesses, my_signer), + ), + )); + } } diff --git a/chain/client/src/test_utils/setup.rs b/chain/client/src/test_utils/setup.rs index be1eaa70bcb..070ee0c88e2 100644 --- a/chain/client/src/test_utils/setup.rs +++ b/chain/client/src/test_utils/setup.rs @@ -45,6 +45,7 @@ use near_network::client::{ }; use near_network::shards_manager::ShardsManagerRequestFromNetwork; use near_network::state_witness::{ + ChunkContractAccessesMessage, ContractCodeRequestMessage, ContractCodeResponseMessage, PartialEncodedStateWitnessForwardMessage, PartialEncodedStateWitnessMessage, PartialWitnessSenderForNetwork, }; @@ -787,6 +788,35 @@ fn process_peer_manager_message_default( } } } + NetworkRequests::ChunkContractAccesses(accounts, accesses) => { + for account in accounts { + for (i, name) in validators.iter().enumerate() { + if name == account { + connectors[i] + .partial_witness_sender + .send(ChunkContractAccessesMessage(accesses.clone())); + } + } + } + } + NetworkRequests::ContractCodeRequest(account, request) => { + for (i, name) in validators.iter().enumerate() { + if name == account { + connectors[i] + .partial_witness_sender + .send(ContractCodeRequestMessage(request.clone())); + } + } + } + NetworkRequests::ContractCodeResponse(account, response) => { + for (i, name) in validators.iter().enumerate() { + if name == account { + connectors[i] + .partial_witness_sender + .send(ContractCodeResponseMessage(response.clone())); + } + } + } NetworkRequests::ForwardTx(_, _) | NetworkRequests::BanPeer { .. } | NetworkRequests::TxStatus(_, _, _) diff --git a/chain/network/src/network_protocol/mod.rs b/chain/network/src/network_protocol/mod.rs index a2a2c28b0e2..95ed0636587 100644 --- a/chain/network/src/network_protocol/mod.rs +++ b/chain/network/src/network_protocol/mod.rs @@ -9,6 +9,9 @@ mod state_sync; pub use edge::*; use near_primitives::stateless_validation::chunk_endorsement::ChunkEndorsement; use near_primitives::stateless_validation::chunk_endorsement::ChunkEndorsementV1; +use near_primitives::stateless_validation::contract_distribution::ChunkContractAccesses; +use near_primitives::stateless_validation::contract_distribution::ContractCodeRequest; +use near_primitives::stateless_validation::contract_distribution::ContractCodeResponse; use near_primitives::stateless_validation::partial_witness::PartialEncodedStateWitness; use near_primitives::stateless_validation::state_witness::ChunkStateWitnessAck; pub use peer::*; @@ -554,6 +557,9 @@ pub enum RoutedMessageBody { EpochSyncRequest, EpochSyncResponse(CompressedEpochSyncProof), StatePartRequest(StatePartRequest), + ChunkContractAccesses(ChunkContractAccesses), + ContractCodeRequest(ContractCodeRequest), + ContractCodeResponse(ContractCodeResponse), } impl RoutedMessageBody { @@ -579,6 +585,8 @@ impl RoutedMessageBody { RoutedMessageBody::ChunkEndorsement(_) | RoutedMessageBody::PartialEncodedStateWitness(_) | RoutedMessageBody::PartialEncodedStateWitnessForward(_) + // TODO(#11099): Remove this when we filter the targets of message at the sender side. + | RoutedMessageBody::ChunkContractAccesses(_) | RoutedMessageBody::VersionedChunkEndorsement(_) => true, _ => false, } @@ -648,6 +656,12 @@ impl fmt::Debug for RoutedMessageBody { write!(f, "EpochSyncResponse") } RoutedMessageBody::StatePartRequest(_) => write!(f, "StatePartRequest"), + // TODO(#11099): Add more details to debug message. + RoutedMessageBody::ChunkContractAccesses(_) => write!(f, "ChunkContractAccesses"), + // TODO(#11099): Add more details to debug message. + RoutedMessageBody::ContractCodeRequest(_) => write!(f, "ContractCodeRequest"), + // TODO(#11099): Add more details to debug message. + RoutedMessageBody::ContractCodeResponse(_) => write!(f, "ContractCodeResponse",), } } } diff --git a/chain/network/src/peer_manager/connection/mod.rs b/chain/network/src/peer_manager/connection/mod.rs index 2035a673da5..ce0a7bc24b7 100644 --- a/chain/network/src/peer_manager/connection/mod.rs +++ b/chain/network/src/peer_manager/connection/mod.rs @@ -47,6 +47,7 @@ impl tcp::Tier { } } + // TODO(#11099): Revisit here for contract code distribution messages. pub(crate) fn is_allowed_routed(self, body: &RoutedMessageBody) -> bool { match body { RoutedMessageBody::BlockApproval(..) diff --git a/chain/network/src/peer_manager/network_state/mod.rs b/chain/network/src/peer_manager/network_state/mod.rs index 9cfa1b4bb41..1219575248a 100644 --- a/chain/network/src/peer_manager/network_state/mod.rs +++ b/chain/network/src/peer_manager/network_state/mod.rs @@ -22,7 +22,8 @@ use crate::routing::NetworkTopologyChange; use crate::shards_manager::ShardsManagerRequestFromNetwork; use crate::snapshot_hosts::{SnapshotHostInfoError, SnapshotHostsCache}; use crate::state_witness::{ - ChunkStateWitnessAckMessage, PartialEncodedStateWitnessForwardMessage, + ChunkContractAccessesMessage, ChunkStateWitnessAckMessage, ContractCodeRequestMessage, + ContractCodeResponseMessage, PartialEncodedStateWitnessForwardMessage, PartialEncodedStateWitnessMessage, PartialWitnessSenderForNetwork, }; use crate::stats::metrics; @@ -804,6 +805,18 @@ impl NetworkState { } None } + RoutedMessageBody::ChunkContractAccesses(accesses) => { + self.partial_witness_adapter.send(ChunkContractAccessesMessage(accesses)); + None + } + RoutedMessageBody::ContractCodeRequest(request) => { + self.partial_witness_adapter.send(ContractCodeRequestMessage(request)); + None + } + RoutedMessageBody::ContractCodeResponse(response) => { + self.partial_witness_adapter.send(ContractCodeResponseMessage(response)); + None + } body => { tracing::error!(target: "network", "Peer received unexpected message type: {:?}", body); None diff --git a/chain/network/src/peer_manager/peer_manager_actor.rs b/chain/network/src/peer_manager/peer_manager_actor.rs index b4739b85fa1..d8268442d0e 100644 --- a/chain/network/src/peer_manager/peer_manager_actor.rs +++ b/chain/network/src/peer_manager/peer_manager_actor.rs @@ -1188,6 +1188,32 @@ impl PeerManagerActor { NetworkResponses::RouteNotFound } } + NetworkRequests::ChunkContractAccesses(chunk_validators, accesses) => { + for chunk_validator in chunk_validators { + self.state.send_message_to_account( + &self.clock, + &chunk_validator, + RoutedMessageBody::ChunkContractAccesses(accesses.clone()), + ); + } + NetworkResponses::NoResponse + } + NetworkRequests::ContractCodeRequest(target, request) => { + self.state.send_message_to_account( + &self.clock, + &target, + RoutedMessageBody::ContractCodeRequest(request), + ); + NetworkResponses::NoResponse + } + NetworkRequests::ContractCodeResponse(target, response) => { + self.state.send_message_to_account( + &self.clock, + &target, + RoutedMessageBody::ContractCodeResponse(response), + ); + NetworkResponses::NoResponse + } } } diff --git a/chain/network/src/rate_limits/messages_limits.rs b/chain/network/src/rate_limits/messages_limits.rs index 54638a829c3..9fddca5776f 100644 --- a/chain/network/src/rate_limits/messages_limits.rs +++ b/chain/network/src/rate_limits/messages_limits.rs @@ -170,6 +170,9 @@ pub enum RateLimitedPeerMessageKey { ChunkStateWitnessAck, PartialEncodedStateWitness, PartialEncodedStateWitnessForward, + ChunkContractAccesses, + ContractCodeRequest, + ContractCodeResponse, } /// Given a `PeerMessage` returns a tuple containing the `RateLimitedPeerMessageKey` @@ -217,6 +220,9 @@ fn get_key_and_token_cost(message: &PeerMessage) -> Option<(RateLimitedPeerMessa RoutedMessageBody::PartialEncodedStateWitnessForward(_) => { Some((PartialEncodedStateWitnessForward, 1)) } + RoutedMessageBody::ChunkContractAccesses(_) => Some((ChunkContractAccesses, 1)), + RoutedMessageBody::ContractCodeRequest(_) => Some((ContractCodeRequest, 1)), + RoutedMessageBody::ContractCodeResponse(_) => Some((ContractCodeResponse, 1)), RoutedMessageBody::VersionedChunkEndorsement(_) => Some((ChunkEndorsement, 1)), RoutedMessageBody::EpochSyncRequest => None, RoutedMessageBody::EpochSyncResponse(_) => None, diff --git a/chain/network/src/state_witness.rs b/chain/network/src/state_witness.rs index 04f699ff972..139568143cd 100644 --- a/chain/network/src/state_witness.rs +++ b/chain/network/src/state_witness.rs @@ -1,5 +1,8 @@ use near_async::messaging::Sender; use near_async::{MultiSend, MultiSendMessage, MultiSenderFrom}; +use near_primitives::stateless_validation::contract_distribution::{ + ChunkContractAccesses, ContractCodeRequest, ContractCodeResponse, +}; use near_primitives::stateless_validation::partial_witness::PartialEncodedStateWitness; use near_primitives::stateless_validation::state_witness::ChunkStateWitnessAck; @@ -15,6 +18,24 @@ pub struct PartialEncodedStateWitnessMessage(pub PartialEncodedStateWitness); #[rtype(result = "()")] pub struct PartialEncodedStateWitnessForwardMessage(pub PartialEncodedStateWitness); +/// Message to partial witness actor (on a chunk validator) that contains code-hashes of +/// the contracts that are accessed when applying the previous chunk. +#[derive(actix::Message, Clone, Debug, PartialEq, Eq)] +#[rtype(result = "()")] +pub struct ChunkContractAccessesMessage(pub ChunkContractAccesses); + +/// Message to partial witness actor (on a chunk producer) that requests contract code +/// by their code hashes. +#[derive(actix::Message, Clone, Debug, PartialEq, Eq)] +#[rtype(result = "()")] +pub struct ContractCodeRequestMessage(pub ContractCodeRequest); + +/// Message to partial witness actor (on a chunk validator) that provides contract code +/// requested beforehand. +#[derive(actix::Message, Clone, Debug, PartialEq, Eq)] +#[rtype(result = "()")] +pub struct ContractCodeResponseMessage(pub ContractCodeResponse); + #[derive(Clone, MultiSend, MultiSenderFrom, MultiSendMessage)] #[multi_send_message_derive(Debug)] #[multi_send_input_derive(Debug, Clone, PartialEq, Eq)] @@ -22,4 +43,7 @@ pub struct PartialWitnessSenderForNetwork { pub chunk_state_witness_ack: Sender, pub partial_encoded_state_witness: Sender, pub partial_encoded_state_witness_forward: Sender, + pub chunk_contract_accesses: Sender, + pub contract_code_request: Sender, + pub contract_code_response: Sender, } diff --git a/chain/network/src/test_loop.rs b/chain/network/src/test_loop.rs index 7bf83f429cf..92ac81a6b02 100644 --- a/chain/network/src/test_loop.rs +++ b/chain/network/src/test_loop.rs @@ -8,7 +8,8 @@ use crate::client::{ }; use crate::shards_manager::ShardsManagerRequestFromNetwork; use crate::state_witness::{ - ChunkStateWitnessAckMessage, PartialEncodedStateWitnessForwardMessage, + ChunkContractAccessesMessage, ChunkStateWitnessAckMessage, ContractCodeRequestMessage, + ContractCodeResponseMessage, PartialEncodedStateWitnessForwardMessage, PartialEncodedStateWitnessMessage, PartialWitnessSenderForNetwork, }; use crate::types::{ @@ -359,6 +360,29 @@ fn network_message_to_partial_witness_handler( } None } + NetworkRequests::ChunkContractAccesses(chunk_validators, contract_accesses) => { + for target in chunk_validators { + shared_state + .senders_for_account(&target) + .partial_witness_sender + .send(ChunkContractAccessesMessage(contract_accesses.clone())); + } + None + } + NetworkRequests::ContractCodeRequest(target, request) => { + shared_state + .senders_for_account(&target) + .partial_witness_sender + .send(ContractCodeRequestMessage(request)); + None + } + NetworkRequests::ContractCodeResponse(target, response) => { + shared_state + .senders_for_account(&target) + .partial_witness_sender + .send(ContractCodeResponseMessage(response)); + None + } _ => Some(request), }) } diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs index 2753883ec1d..35ba69ebe28 100644 --- a/chain/network/src/types.rs +++ b/chain/network/src/types.rs @@ -21,6 +21,9 @@ use near_primitives::hash::CryptoHash; use near_primitives::network::{AnnounceAccount, PeerId}; use near_primitives::sharding::PartialEncodedChunkWithArcReceipts; use near_primitives::stateless_validation::chunk_endorsement::ChunkEndorsement; +use near_primitives::stateless_validation::contract_distribution::{ + ChunkContractAccesses, ContractCodeRequest, ContractCodeResponse, +}; use near_primitives::stateless_validation::partial_witness::PartialEncodedStateWitness; use near_primitives::stateless_validation::state_witness::ChunkStateWitnessAck; use near_primitives::transaction::SignedTransaction; @@ -291,6 +294,15 @@ pub enum NetworkRequests { EpochSyncRequest { peer_id: PeerId }, /// Response to an epoch sync request EpochSyncResponse { route_back: CryptoHash, proof: CompressedEpochSyncProof }, + /// Message from chunk producer to chunk validators containing the code-hashes of contracts + /// accessed for the main state transition in the witness. + ChunkContractAccesses(Vec, ChunkContractAccesses), + /// Message from chunk validator to chunk producer to request missing contract code. + /// This message is currently sent as a result of receiving the ChunkContractAccesses message + /// and failing to find the corresponding code for the hashes received. + ContractCodeRequest(AccountId, ContractCodeRequest), + /// Message from chunk producer to chunk validators to send the contract code as response to ContractCodeRequest. + ContractCodeResponse(AccountId, ContractCodeResponse), } #[derive(Debug, actix::Message, strum::IntoStaticStr)] diff --git a/core/primitives-core/src/version.rs b/core/primitives-core/src/version.rs index 508c8265998..3338ac92e83 100644 --- a/core/primitives-core/src/version.rs +++ b/core/primitives-core/src/version.rs @@ -174,6 +174,8 @@ pub enum ProtocolFeature { StateStoredReceipt, /// Resharding V3 SimpleNightshadeV4, + /// Exclude contract code from the chunk state witness and distribute it to chunk validators separately. + ExcludeContractCodeFromStateWitness, } impl ProtocolFeature { @@ -251,6 +253,11 @@ impl ProtocolFeature { // that always enables this for mocknet (see config_mocknet function). ProtocolFeature::ShuffleShardAssignments => 143, ProtocolFeature::SimpleNightshadeV4 => 145, + + // Features that are not yet in Nightly. + + // TODO(#11099): Move this feature to Nightly. + ProtocolFeature::ExcludeContractCodeFromStateWitness => 146, } } diff --git a/core/primitives/src/stateless_validation/contract_distribution.rs b/core/primitives/src/stateless_validation/contract_distribution.rs new file mode 100644 index 00000000000..16c796e95ef --- /dev/null +++ b/core/primitives/src/stateless_validation/contract_distribution.rs @@ -0,0 +1,236 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use bytesize::ByteSize; +use near_crypto::Signature; +use near_primitives_core::hash::CryptoHash; +use near_schema_checker_lib::ProtocolSchema; + +use crate::{utils::compression::CompressedData, validator_signer::ValidatorSigner}; + +use super::{ChunkProductionKey, SignatureDifferentiator}; + +/// Contains contracts (as code-hashes) accessed during the application of a chunk. +/// This is used by the chunk producer to let the chunk validators know about which contracts +/// are needed for validating a witness, so that the chunk validators can request missing code. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub enum ChunkContractAccesses { + V1(ChunkContractAccessesV1), +} + +impl ChunkContractAccesses { + pub fn new( + next_chunk: ChunkProductionKey, + contracts: Vec, + signer: &ValidatorSigner, + ) -> Self { + Self::V1(ChunkContractAccessesV1::new(next_chunk, contracts, signer)) + } + + pub fn contracts(&self) -> &Vec { + match self { + Self::V1(accesses) => &accesses.inner.contracts, + } + } + + pub fn chunk_production_key(&self) -> &ChunkProductionKey { + match self { + Self::V1(accesses) => &accesses.inner.next_chunk, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct ChunkContractAccessesV1 { + inner: ChunkContractAccessesInner, + /// Signature of the inner, signed by the chunk producer of the next chunk. + signature: Signature, +} + +impl ChunkContractAccessesV1 { + fn new( + next_chunk: ChunkProductionKey, + contracts: Vec, + signer: &ValidatorSigner, + ) -> Self { + let inner = ChunkContractAccessesInner::new(next_chunk, contracts); + let signature = signer.sign_chunk_contract_accesses(&inner); + Self { inner, signature } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct ChunkContractAccessesInner { + /// Production metadata of the chunk created after the chunk the accesses belong to. + /// We associate this message with the next-chunk info because this message is generated + /// and distributed while generating the state-witness of the next chunk + /// (by the chunk producer of the next chunk). + // TODO(#11099): Consider simplifying this with the ChunkHash of the prev_chunk (the one the accesses belong to). + next_chunk: ChunkProductionKey, + /// List of code-hashes for the contracts accessed. + contracts: Vec, + signature_differentiator: SignatureDifferentiator, +} + +impl ChunkContractAccessesInner { + fn new(next_chunk: ChunkProductionKey, contracts: Vec) -> Self { + Self { + next_chunk, + contracts, + signature_differentiator: "ChunkContractAccessesInner".to_owned(), + } + } +} + +// Data structures for chunk validators to request contract code from chunk producers. + +/// Message to request missing code for a set of contracts. +/// The contracts are idenfied by the hash of their code. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub enum ContractCodeRequest { + V1(ContractCodeRequestV1), +} + +impl ContractCodeRequest { + pub fn new( + next_chunk: ChunkProductionKey, + contracts: Vec, + signer: &ValidatorSigner, + ) -> Self { + Self::V1(ContractCodeRequestV1::new(next_chunk, contracts, signer)) + } + + pub fn contracts(&self) -> &Vec { + match self { + Self::V1(request) => &request.inner.contracts, + } + } + + pub fn chunk_production_key(&self) -> &ChunkProductionKey { + match self { + Self::V1(request) => &request.inner.next_chunk, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct ContractCodeRequestV1 { + inner: ContractCodeRequestInner, + /// Signature of the inner. + signature: Signature, +} + +impl ContractCodeRequestV1 { + fn new( + next_chunk: ChunkProductionKey, + contracts: Vec, + signer: &ValidatorSigner, + ) -> Self { + let inner = ContractCodeRequestInner::new(next_chunk, contracts); + let signature = signer.sign_contract_code_request(&inner); + Self { inner, signature } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct ContractCodeRequestInner { + /// Production metadata of the chunk created after the chunk the accesses belong to. + /// We associate this message with the next-chunk info because this message is generated + /// and distributed while generating the state-witness of the next chunk + /// (by the chunk producer of the next chunk). + // TODO(#11099): Consider simplifying this with the ChunkHash of the prev_chunk (the one the accesses belong to). + next_chunk: ChunkProductionKey, + /// List of code-hashes for the contracts accessed. + contracts: Vec, + signature_differentiator: SignatureDifferentiator, +} + +impl ContractCodeRequestInner { + fn new(next_chunk: ChunkProductionKey, contracts: Vec) -> Self { + Self { + next_chunk, + contracts, + signature_differentiator: "ContractCodeRequestInner".to_owned(), + } + } +} + +// Data structures for chunk producers to send contract code to chunk validators as response to ContractCodeRequest. + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub enum ContractCodeResponse { + V1(ContractCodeResponseV1), +} + +impl ContractCodeResponse { + pub fn new(contracts: &Vec, signer: &ValidatorSigner) -> Self { + Self::V1(ContractCodeResponseV1::new(contracts, signer)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct ContractCodeResponseV1 { + inner: ContractCodeResponseInner, + /// Signature of the inner. + signature: Signature, +} + +impl ContractCodeResponseV1 { + fn new(contracts: &Vec, signer: &ValidatorSigner) -> Self { + let inner = ContractCodeResponseInner::new(contracts); + let signature = signer.sign_contract_code_response(&inner); + Self { inner, signature } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct ContractCodeResponseInner { + /// Code for the contracts. + compressed_contracts: CompressedContractCode, + signature_differentiator: SignatureDifferentiator, +} + +impl ContractCodeResponseInner { + fn new(contracts: &Vec) -> Self { + let (compressed_contracts, _size) = CompressedContractCode::encode(&contracts).unwrap(); + Self { + compressed_contracts, + signature_differentiator: "ContractCodeResponseInner".to_owned(), + } + } +} + +/// Represents max allowed size of the raw (not compressed) contract code response, +/// corresponds to the size of borsh-serialized ContractCodeResponse. +const MAX_UNCOMPRESSED_CONTRACT_CODE_RESPONSE_SIZE: u64 = + ByteSize::mib(if cfg!(feature = "test_features") { 512 } else { 64 }).0; +const CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL: i32 = 3; + +/// This is the compressed version of a list of borsh-serialized contract code. +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshSerialize, + BorshDeserialize, + ProtocolSchema, + derive_more::From, + derive_more::AsRef, +)] +struct CompressedContractCode(Box<[u8]>); + +impl + CompressedData< + Vec, + MAX_UNCOMPRESSED_CONTRACT_CODE_RESPONSE_SIZE, + CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL, + > for CompressedContractCode +{ +} + +/// Hash of some (uncompiled) contract code. +#[derive(Debug, Clone, Hash, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct CodeHash(pub CryptoHash); + +/// Raw bytes of the (uncompiled) contract code. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct CodeBytes(Vec); diff --git a/core/primitives/src/stateless_validation/mod.rs b/core/primitives/src/stateless_validation/mod.rs index 6526346c414..72cbdaeb912 100644 --- a/core/primitives/src/stateless_validation/mod.rs +++ b/core/primitives/src/stateless_validation/mod.rs @@ -1,9 +1,12 @@ +use borsh::{BorshDeserialize, BorshSerialize}; use near_primitives_core::types::{BlockHeight, ShardId}; +use near_schema_checker_lib::ProtocolSchema; use crate::types::EpochId; pub mod chunk_endorsement; pub mod chunk_endorsements_bitmap; +pub mod contract_distribution; pub mod partial_witness; pub mod state_witness; pub mod stored_chunk_state_transition_data; @@ -19,7 +22,7 @@ type SignatureDifferentiator = String; /// This struct contains combination of fields that uniquely identify chunk production. /// It means that for a given instance only one chunk could be produced. -#[derive(Debug, Hash, PartialEq, Eq, Clone)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize, ProtocolSchema)] pub struct ChunkProductionKey { pub shard_id: ShardId, pub epoch_id: EpochId, diff --git a/core/primitives/src/stateless_validation/stored_chunk_state_transition_data.rs b/core/primitives/src/stateless_validation/stored_chunk_state_transition_data.rs index 0b0bb96ab90..a76b42f115e 100644 --- a/core/primitives/src/stateless_validation/stored_chunk_state_transition_data.rs +++ b/core/primitives/src/stateless_validation/stored_chunk_state_transition_data.rs @@ -3,10 +3,17 @@ use borsh::{BorshDeserialize, BorshSerialize}; use near_primitives_core::hash::CryptoHash; use near_schema_checker_lib::ProtocolSchema; +use super::contract_distribution::CodeHash; + /// Stored on disk for each chunk, including missing chunks, in order to /// produce a chunk state witness when needed. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] -pub struct StoredChunkStateTransitionData { +pub enum StoredChunkStateTransitionData { + V1(StoredChunkStateTransitionDataV1), +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] +pub struct StoredChunkStateTransitionDataV1 { /// The partial state that is needed to apply the state transition, /// whether it is a new chunk state transition or a implicit missing chunk /// state transition. @@ -16,4 +23,6 @@ pub struct StoredChunkStateTransitionData { /// but is used to validate against `StateChunkWitness::exact_receipts_hash` /// to ease debugging of why a state witness may be incorrect. pub receipts_hash: CryptoHash, + /// The code-hashes of the contracts that are accessed (called) during the state transition. + pub contract_accesses: Vec, } diff --git a/core/primitives/src/validator_signer.rs b/core/primitives/src/validator_signer.rs index d92a6ee1db6..948fee07996 100644 --- a/core/primitives/src/validator_signer.rs +++ b/core/primitives/src/validator_signer.rs @@ -12,6 +12,9 @@ use crate::sharding::ChunkHash; use crate::stateless_validation::chunk_endorsement::{ ChunkEndorsementInner, ChunkEndorsementMetadata, }; +use crate::stateless_validation::contract_distribution::{ + ChunkContractAccessesInner, ContractCodeRequestInner, ContractCodeResponseInner, +}; use crate::stateless_validation::partial_witness::PartialEncodedStateWitnessInner; use crate::stateless_validation::state_witness::EncodedChunkStateWitness; use crate::telemetry::TelemetryInfo; @@ -146,6 +149,30 @@ impl ValidatorSigner { } } + /// Signs the inner contents of a ChunkContractAccesses message. + pub fn sign_chunk_contract_accesses(&self, inner: &ChunkContractAccessesInner) -> Signature { + match self { + ValidatorSigner::Empty(signer) => signer.sign_chunk_contract_accesses(inner), + ValidatorSigner::InMemory(signer) => signer.sign_chunk_contract_accesses(inner), + } + } + + /// Signs the inner contents of a ContractCodeRequest message. + pub fn sign_contract_code_request(&self, inner: &ContractCodeRequestInner) -> Signature { + match self { + ValidatorSigner::Empty(signer) => signer.sign_contract_code_request(inner), + ValidatorSigner::InMemory(signer) => signer.sign_contract_code_request(inner), + } + } + + /// Signs the inner contents of a ContractCodeResponse message. + pub fn sign_contract_code_response(&self, inner: &ContractCodeResponseInner) -> Signature { + match self { + ValidatorSigner::Empty(signer) => signer.sign_contract_code_response(inner), + ValidatorSigner::InMemory(signer) => signer.sign_contract_code_response(inner), + } + } + /// Signs a proto-serialized AccountKeyPayload (see /// chain/network/src/network_protocol/network.proto). /// Making it typesafe would require moving the definition of @@ -273,6 +300,18 @@ impl EmptyValidatorSigner { fn sign_account_key_payload(&self, _proto_bytes: &[u8]) -> Signature { Signature::default() } + + fn sign_chunk_contract_accesses(&self, _inner: &ChunkContractAccessesInner) -> Signature { + Signature::default() + } + + fn sign_contract_code_request(&self, _inner: &ContractCodeRequestInner) -> Signature { + Signature::default() + } + + fn sign_contract_code_response(&self, _inner: &ContractCodeResponseInner) -> Signature { + Signature::default() + } } /// Signer that keeps secret key in memory and signs locally. @@ -378,6 +417,18 @@ impl InMemoryValidatorSigner { self.signer.sign(proto_bytes) } + fn sign_chunk_contract_accesses(&self, inner: &ChunkContractAccessesInner) -> Signature { + self.signer.sign(&borsh::to_vec(inner).unwrap()) + } + + fn sign_contract_code_request(&self, inner: &ContractCodeRequestInner) -> Signature { + self.signer.sign(&borsh::to_vec(inner).unwrap()) + } + + fn sign_contract_code_response(&self, inner: &ContractCodeResponseInner) -> Signature { + self.signer.sign(&borsh::to_vec(inner).unwrap()) + } + fn compute_vrf_with_proof( &self, data: &[u8], diff --git a/core/store/src/contract.rs b/core/store/src/contract.rs index b0f5ffca0a5..9e334f5741b 100644 --- a/core/store/src/contract.rs +++ b/core/store/src/contract.rs @@ -1,7 +1,8 @@ use crate::TrieStorage; use near_primitives::hash::CryptoHash; +use near_primitives::stateless_validation::contract_distribution::CodeHash; use near_vm_runner::ContractCode; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::sync::{Arc, Mutex}; /// Reads contract code from the trie by its hash. @@ -24,11 +25,25 @@ pub struct ContractStorage { /// adjusted to write out the contract into the relevant part of the database immediately /// (without going through transactional storage operations and such). uncommitted_deploys: Arc>>, + + /// List of code-hashes for the contracts retrieved from the storage. + /// This does not include the contracts deployed and then read. + // TODO(#11099): For collecting deployed code, consolidate this with uncommitted_deploys. + storage_reads: Arc>>>, +} + +/// Result of finalizing the contract storage. +pub struct ContractStorageResult { + pub contract_accesses: Vec, } impl ContractStorage { pub fn new(storage: Arc) -> Self { - Self { storage, uncommitted_deploys: Default::default() } + Self { + storage, + uncommitted_deploys: Default::default(), + storage_reads: Arc::new(Mutex::new(Some(HashSet::new()))), + } } pub fn get(&self, code_hash: CryptoHash) -> Option { @@ -39,14 +54,31 @@ impl ContractStorage { } } - match self.storage.retrieve_raw_bytes(&code_hash) { + let contract_code = match self.storage.retrieve_raw_bytes(&code_hash) { Ok(raw_code) => Some(ContractCode::new(raw_code.to_vec(), Some(code_hash))), Err(_) => None, + }; + + if contract_code.is_some() { + let mut guard = self.storage_reads.lock().expect("no panics"); + guard.as_mut().expect("must not be called after finalize").insert(CodeHash(code_hash)); } + + contract_code } pub fn store(&self, code: ContractCode) { let mut guard = self.uncommitted_deploys.lock().expect("no panics"); guard.insert(*code.hash(), code); } + + /// Destructs the ContractStorage and returns the list of storage reads. + pub(crate) fn finalize(self) -> ContractStorageResult { + let mut guard = self.storage_reads.lock().expect("no panics"); + // TODO(#11099): Change `replace` to `take` after investigating why `get` is called after the TrieUpdate + // is finalizing in the yield-resume tests. + ContractStorageResult { + contract_accesses: guard.replace(HashSet::new()).unwrap().into_iter().collect(), + } + } } diff --git a/core/store/src/genesis/state_applier.rs b/core/store/src/genesis/state_applier.rs index 349c19b432c..757d74874e8 100644 --- a/core/store/src/genesis/state_applier.rs +++ b/core/store/src/genesis/state_applier.rs @@ -1,5 +1,6 @@ use crate::adapter::StoreUpdateAdapter; use crate::flat::FlatStateChanges; +use crate::trie::update::TrieUpdateResult; use crate::{ get_account, has_received_data, set, set_access_key, set_account, set_code, set_delayed_receipt, set_postponed_receipt, set_promise_yield_receipt, set_received_data, @@ -139,7 +140,7 @@ impl<'a> AutoFlushingTrieUpdate<'a> { ); let mut old_state_update = state_update.take().expect("state update should be set"); old_state_update.commit(StateChangeCause::InitialState); - let (_, trie_changes, state_changes) = + let TrieUpdateResult { trie_changes, state_changes, .. } = old_state_update.finalize().expect("Genesis state update failed"); let mut store_update = self.tries.store_update(); *state_root = self.tries.apply_all(&trie_changes, self.shard_uid, &mut store_update); diff --git a/core/store/src/metadata.rs b/core/store/src/metadata.rs index a07581e3922..eec060fdd5c 100644 --- a/core/store/src/metadata.rs +++ b/core/store/src/metadata.rs @@ -2,7 +2,7 @@ pub type DbVersion = u32; /// Current version of the database. -pub const DB_VERSION: DbVersion = 40; +pub const DB_VERSION: DbVersion = 41; /// Database version at which point DbKind was introduced. const DB_VERSION_WITH_KIND: DbVersion = 34; diff --git a/core/store/src/migrations.rs b/core/store/src/migrations.rs index e80cd7e6ec6..faa4214d561 100644 --- a/core/store/src/migrations.rs +++ b/core/store/src/migrations.rs @@ -1,10 +1,14 @@ use crate::metadata::DbKind; use crate::{DBCol, Store, StoreUpdate}; use borsh::{BorshDeserialize, BorshSerialize}; +use near_primitives::challenge::PartialState; use near_primitives::epoch_manager::EpochSummary; use near_primitives::epoch_manager::AGGREGATOR_KEY; use near_primitives::hash::CryptoHash; use near_primitives::state::FlatStateValue; +use near_primitives::stateless_validation::stored_chunk_state_transition_data::{ + StoredChunkStateTransitionData, StoredChunkStateTransitionDataV1, +}; use near_primitives::transaction::{ExecutionOutcomeWithIdAndProof, ExecutionOutcomeWithProof}; use near_primitives::types::{ validator_stake::ValidatorStake, AccountId, EpochId, ShardId, ValidatorId, @@ -358,3 +362,32 @@ pub fn migrate_39_to_40(store: &Store) -> anyhow::Result<()> { update.commit()?; Ok(()) } + +/// Migrates the database from version 39 to 40. +/// +/// The migraton replaces non-enum StoredChunkStateTransitionData struct with its enum version. +pub fn migrate_40_to_41(store: &Store) -> anyhow::Result<()> { + #[derive(BorshDeserialize)] + pub struct DeprecatedStoredChunkStateTransitionData { + pub base_state: PartialState, + pub receipts_hash: CryptoHash, + } + + let _span = + tracing::info_span!(target: "migrations", "Replacing StoredChunkStateTransitionData with its enum version V1").entered(); + let mut update = store.store_update(); + for result in store.iter(DBCol::StateTransitionData) { + let (key, old_value) = result?; + let DeprecatedStoredChunkStateTransitionData { base_state, receipts_hash } = + DeprecatedStoredChunkStateTransitionData::try_from_slice(&old_value)?; + let new_value = + borsh::to_vec(&StoredChunkStateTransitionData::V1(StoredChunkStateTransitionDataV1 { + base_state, + receipts_hash, + contract_accesses: vec![], + }))?; + update.set(DBCol::StateTransitionData, &key, &new_value); + } + update.commit()?; + Ok(()) +} diff --git a/core/store/src/trie/mem/loading.rs b/core/store/src/trie/mem/loading.rs index 32994789d67..5e1478b8402 100644 --- a/core/store/src/trie/mem/loading.rs +++ b/core/store/src/trie/mem/loading.rs @@ -188,6 +188,7 @@ mod tests { use crate::trie::mem::loading::load_trie_from_flat_state; use crate::trie::mem::lookup::memtrie_lookup; use crate::trie::mem::nibbles_utils::{all_two_nibble_nibbles, multi_hex_to_nibbles}; + use crate::trie::update::TrieUpdateResult; use crate::{DBCol, KeyLookupMode, NibbleSlice, ShardTries, Store, Trie, TrieUpdate}; use near_primitives::congestion_info::CongestionInfo; use near_primitives::hash::CryptoHash; @@ -484,7 +485,7 @@ mod tests { } trie_update .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); - let (_, trie_changes, state_changes) = trie_update.finalize().unwrap(); + let TrieUpdateResult { trie_changes, state_changes, .. } = trie_update.finalize().unwrap(); let mut store_update = tries.store_update(); tries.apply_insertions(&trie_changes, shard_uid, &mut store_update); store_update.store_update().merge( diff --git a/core/store/src/trie/resharding_v2.rs b/core/store/src/trie/resharding_v2.rs index 7a15243dd7d..94cfd552e9c 100644 --- a/core/store/src/trie/resharding_v2.rs +++ b/core/store/src/trie/resharding_v2.rs @@ -16,6 +16,8 @@ use near_primitives::trie_key::TrieKey; use near_primitives::types::{StateChangeCause, StateRoot}; use std::collections::HashMap; +use super::update::TrieUpdateResult; + impl ShardTries { /// add `values` (key-value pairs of items stored in states) to build states for new shards /// `state_roots` contains state roots for the new shards @@ -123,7 +125,7 @@ impl ShardTries { let mut new_state_roots = HashMap::new(); let mut store_update = self.store_update(); for (shard_uid, update) in updates { - let (_, trie_changes, state_changes) = update.finalize()?; + let TrieUpdateResult { trie_changes, state_changes, .. } = update.finalize()?; let state_root = self.apply_all(&trie_changes, shard_uid, &mut store_update); FlatStateChanges::from_state_changes(&state_changes) .apply_to_flat_state(&mut store_update.flat_store_update(), shard_uid); @@ -463,7 +465,7 @@ mod tests { delayed_receipt_indices.next_available_index = all_receipts.len() as u64; set(&mut trie_update, TrieKey::DelayedReceiptIndices, &delayed_receipt_indices); trie_update.commit(StateChangeCause::ReshardingV2); - let (_, trie_changes, _) = trie_update.finalize().unwrap(); + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let state_root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); @@ -518,7 +520,7 @@ mod tests { promise_yield_indices.next_available_index = all_timeouts.len() as u64; set(&mut trie_update, TrieKey::PromiseYieldIndices, &promise_yield_indices); trie_update.commit(StateChangeCause::ReshardingV2); - let (_, trie_changes, _) = trie_update.finalize().unwrap(); + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let state_root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); diff --git a/core/store/src/trie/update.rs b/core/store/src/trie/update.rs index a369c8e0db2..df258419b7f 100644 --- a/core/store/src/trie/update.rs +++ b/core/store/src/trie/update.rs @@ -4,6 +4,7 @@ use super::{OptimizedValueRef, Trie, TrieWithReadLock}; use crate::contract::ContractStorage; use crate::trie::{KeyLookupMode, TrieChanges}; use crate::StorageError; +use near_primitives::stateless_validation::contract_distribution::CodeHash; use near_primitives::trie_key::TrieKey; use near_primitives::types::{ RawStateChange, RawStateChanges, RawStateChangesWithTrieKey, StateChangeCause, StateRoot, @@ -52,6 +53,14 @@ impl<'a> TrieUpdateValuePtr<'a> { } } +/// Contains the result of trie updates generated during the finalization of [`TrieUpdate`]. +pub struct TrieUpdateResult { + pub trie: Trie, + pub trie_changes: TrieChanges, + pub state_changes: Vec, + pub contract_accesses: Vec, +} + impl TrieUpdate { pub fn new(trie: Trie) -> Self { let trie_storage = trie.storage.clone(); @@ -156,12 +165,10 @@ impl TrieUpdate { db_reads = tracing::field::Empty ) )] - pub fn finalize( - self, - ) -> Result<(Trie, TrieChanges, Vec), StorageError> { + pub fn finalize(self) -> Result { assert!(self.prospective.is_empty(), "Finalize cannot be called with uncommitted changes."); let span = tracing::Span::current(); - let TrieUpdate { trie, committed, .. } = self; + let TrieUpdate { trie, committed, contract_storage, .. } = self; let start_counts = trie.accounting_cache.borrow().get_trie_nodes_count(); let mut state_changes = Vec::with_capacity(committed.len()); let trie_changes = @@ -180,7 +187,8 @@ impl TrieUpdate { span.record("mem_reads", iops_delta.mem_reads); span.record("db_reads", iops_delta.db_reads); } - Ok((trie, trie_changes, state_changes)) + let contract_accesses = contract_storage.finalize().contract_accesses; + Ok(TrieUpdateResult { trie, trie_changes, state_changes, contract_accesses }) } /// Returns Error if the underlying storage fails @@ -275,7 +283,7 @@ mod tests { trie_update.set(test_key(b"xxx".to_vec()), b"puppy".to_vec()); trie_update .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); store_update.commit().unwrap(); @@ -300,7 +308,7 @@ mod tests { let mut trie_update = tries.new_trie_update(COMPLEX_SHARD_UID, Trie::EMPTY_ROOT); trie_update.remove(test_key(b"dog".to_vec())); trie_update.commit(StateChangeCause::TransactionProcessing { tx_hash: Trie::EMPTY_ROOT }); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); store_update.commit().unwrap(); @@ -312,7 +320,7 @@ mod tests { trie_update.remove(test_key(b"dog".to_vec())); trie_update .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); store_update.commit().unwrap(); @@ -323,7 +331,7 @@ mod tests { trie_update.set(test_key(b"dog".to_vec()), b"puppy".to_vec()); trie_update .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); store_update.commit().unwrap(); @@ -332,7 +340,7 @@ mod tests { trie_update.remove(test_key(b"dog".to_vec())); trie_update .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); store_update.commit().unwrap(); @@ -347,7 +355,7 @@ mod tests { trie_update.set(test_key(b"aaa".to_vec()), b"puppy".to_vec()); trie_update .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); store_update.commit().unwrap(); diff --git a/genesis-tools/genesis-populate/src/lib.rs b/genesis-tools/genesis-populate/src/lib.rs index ee223645c5b..c2a80d950bd 100644 --- a/genesis-tools/genesis-populate/src/lib.rs +++ b/genesis-tools/genesis-populate/src/lib.rs @@ -26,6 +26,7 @@ use near_primitives::utils::to_timestamp; use near_primitives::version::ProtocolFeature; use near_store::adapter::StoreUpdateAdapter; use near_store::genesis::{compute_storage_usage, initialize_genesis_state}; +use near_store::trie::update::TrieUpdateResult; use near_store::{ get_account, get_genesis_state_roots, set_access_key, set_account, set_code, Store, TrieUpdate, }; @@ -205,7 +206,7 @@ impl GenesisBuilder { } let tries = self.runtime.get_tries(); state_update.commit(StateChangeCause::InitialState); - let (_, trie_changes, state_changes) = state_update.finalize()?; + let TrieUpdateResult { trie_changes, state_changes, .. } = state_update.finalize()?; let genesis_shard_version = self.genesis.config.shard_layout.version(); let shard_uid = ShardUId { version: genesis_shard_version, shard_id: shard_id_as_u32(shard_idx) }; diff --git a/integration-tests/src/tests/runtime/state_viewer.rs b/integration-tests/src/tests/runtime/state_viewer.rs index 8c71577aa72..e8e12282f3b 100644 --- a/integration-tests/src/tests/runtime/state_viewer.rs +++ b/integration-tests/src/tests/runtime/state_viewer.rs @@ -284,7 +284,7 @@ fn test_view_state() { b"321".to_vec(), ); state_update.commit(StateChangeCause::InitialState); - let trie_changes = state_update.finalize().unwrap().1; + let trie_changes = state_update.finalize().unwrap().trie_changes; let mut db_changes = tries.store_update(); let new_root = tries.apply_all(&trie_changes, shard_uid, &mut db_changes); db_changes.commit().unwrap(); diff --git a/nearcore/src/entity_debug.rs b/nearcore/src/entity_debug.rs index ec633b4f3b7..37394ddc873 100644 --- a/nearcore/src/entity_debug.rs +++ b/nearcore/src/entity_debug.rs @@ -22,7 +22,9 @@ use near_primitives::shard_layout::get_block_shard_uid; use near_primitives::sharding::ShardChunk; use near_primitives::state::FlatStateValue; use near_primitives::state_sync::StateSyncDumpProgress; -use near_primitives::stateless_validation::stored_chunk_state_transition_data::StoredChunkStateTransitionData; +use near_primitives::stateless_validation::stored_chunk_state_transition_data::{ + StoredChunkStateTransitionData, StoredChunkStateTransitionDataV1, +}; use near_primitives::transaction::{ExecutionOutcomeWithProof, SignedTransaction}; use near_primitives::types::chunk_extra::ChunkExtra; use near_primitives::types::{AccountId, Balance, BlockHeight, StateRoot}; @@ -324,17 +326,24 @@ impl EntityDebugHandlerImpl { &get_block_shard_id(&block_hash, shard_id), )? .ok_or_else(|| anyhow!("State transition not found"))?; - let mut serialized = EntityDataStruct::new(); - serialized.add( - "base_state", - PartialStateParser::parse_and_serialize_partial_state( - state_transition.base_state, - ), - ); - serialized - .add("receipts_hash", serialize_entity(&state_transition.receipts_hash)); - state_transitions - .add(&shard_id.to_string(), EntityDataValue::Struct(serialized.into())); + match state_transition { + StoredChunkStateTransitionData::V1(StoredChunkStateTransitionDataV1 { + base_state, + receipts_hash, + .. + }) => { + let mut serialized = EntityDataStruct::new(); + serialized.add( + "base_state", + PartialStateParser::parse_and_serialize_partial_state(base_state), + ); + serialized.add("receipts_hash", serialize_entity(&receipts_hash)); + state_transitions.add( + &shard_id.to_string(), + EntityDataValue::Struct(serialized.into()), + ); + } + } } Ok(EntityDataValue::Struct(state_transitions.into())) } diff --git a/nearcore/src/migrations.rs b/nearcore/src/migrations.rs index fbf5dc9256f..83c26937779 100644 --- a/nearcore/src/migrations.rs +++ b/nearcore/src/migrations.rs @@ -86,6 +86,7 @@ impl<'a> near_store::StoreMigrator for Migrator<'a> { 37 => near_store::migrations::migrate_37_to_38(store), 38 => near_store::migrations::migrate_38_to_39(store), 39 => near_store::migrations::migrate_39_to_40(store), + 40 => near_store::migrations::migrate_40_to_41(store), DB_VERSION.. => unreachable!(), } } diff --git a/pytest/lib/messages/network.py b/pytest/lib/messages/network.py index 811afb711ba..3a009ecca53 100644 --- a/pytest/lib/messages/network.py +++ b/pytest/lib/messages/network.py @@ -397,6 +397,7 @@ class AdvertisedPeerDistance: PartialEncodedStateWitness ], ['VersionedChunkEndorsement', ChunkEndorsement], + # TODO(11099): Update the schema with the new messages for contract distribution. ] } ], diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 678ab116d67..25ef43db20f 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -183,7 +183,11 @@ pub(crate) fn action_function_call( ) .into()); } - state_update.trie.request_code_recording(account_id.clone()); + if !ProtocolFeature::ExcludeContractCodeFromStateWitness + .enabled(apply_state.current_protocol_version) + { + state_update.trie.request_code_recording(account_id.clone()); + } #[cfg(feature = "test_features")] apply_recorded_storage_garbage(function_call, state_update); @@ -1436,7 +1440,7 @@ mod tests { set_access_key(&mut state_update, account_id.clone(), public_key.clone(), access_key); state_update.commit(StateChangeCause::InitialState); - let trie_changes = state_update.finalize().unwrap().1; + let trie_changes = state_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); store_update.commit().unwrap(); diff --git a/runtime/runtime/src/balance_checker.rs b/runtime/runtime/src/balance_checker.rs index 2baeabfadff..f5999852bb1 100644 --- a/runtime/runtime/src/balance_checker.rs +++ b/runtime/runtime/src/balance_checker.rs @@ -463,7 +463,7 @@ mod tests { let mut trie_update = tries.new_trie_update(shard_uid, Trie::EMPTY_ROOT); set_initial_state(&mut trie_update); trie_update.commit(StateChangeCause::NotWritableToDisk); - let trie_changes = trie_update.finalize().unwrap().1; + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); store_update.commit().unwrap(); diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 1dba5601085..de3bb5ffae9 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -32,6 +32,7 @@ use near_primitives::receipt::{ use near_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; use near_primitives::sandbox::state_patch::SandboxStatePatch; use near_primitives::state_record::StateRecord; +use near_primitives::stateless_validation::contract_distribution::CodeHash; #[cfg(feature = "protocol_feature_nonrefundable_transfer_nep491")] use near_primitives::transaction::NonrefundableStorageTransferAction; use near_primitives::transaction::{ @@ -52,6 +53,7 @@ use near_primitives::utils::{ use near_primitives::version::{ProtocolFeature, ProtocolVersion}; use near_primitives_core::apply::ApplyChunkReason; use near_store::trie::receipts_column_helper::DelayedReceiptQueue; +use near_store::trie::update::TrieUpdateResult; use near_store::{ get, get_account, get_postponed_receipt, get_promise_yield_receipt, get_pure, get_received_data, has_received_data, remove_account, remove_postponed_receipt, @@ -189,6 +191,7 @@ pub struct ApplyResult { pub delayed_receipts_count: u64, pub metrics: Option, pub congestion_info: Option, + pub contract_accesses: Vec, } #[derive(Debug)] @@ -2051,7 +2054,8 @@ impl Runtime { metrics::CHUNK_RECORDED_SIZE_UPPER_BOUND .with_label_values(&[shard_id_str.as_str()]) .observe(chunk_recorded_size_upper_bound); - let (trie, trie_changes, state_changes) = state_update.finalize()?; + let TrieUpdateResult { trie, trie_changes, state_changes, contract_accesses } = + state_update.finalize()?; if let Some(prefetcher) = &processing_state.prefetcher { // Only clear the prefetcher queue after finalize is done because as part of receipt @@ -2106,6 +2110,7 @@ impl Runtime { delayed_receipts_count, metrics: Some(processing_state.metrics), congestion_info: own_congestion_info, + contract_accesses, }) } } @@ -2192,7 +2197,8 @@ fn missing_chunk_apply_result( delayed_receipts: &DelayedReceiptQueueWrapper, processing_state: ApplyProcessingState, ) -> Result { - let (trie, trie_changes, state_changes) = processing_state.state_update.finalize()?; + let TrieUpdateResult { trie, trie_changes, state_changes, contract_accesses } = + processing_state.state_update.finalize()?; let proof = trie.recorded_storage(); // For old chunks, copy the congestion info exactly as it came in, @@ -2218,6 +2224,7 @@ fn missing_chunk_apply_result( delayed_receipts_count: delayed_receipts.len(), metrics: None, congestion_info, + contract_accesses, }); } diff --git a/runtime/runtime/src/tests/apply.rs b/runtime/runtime/src/tests/apply.rs index f4a8cd3bd61..b5c57362454 100644 --- a/runtime/runtime/src/tests/apply.rs +++ b/runtime/runtime/src/tests/apply.rs @@ -73,7 +73,7 @@ fn setup_runtime_for_shard( set_account(&mut initial_state, account_id.clone(), &initial_account); set_access_key(&mut initial_state, account_id, signer.public_key(), &AccessKey::full_access()); initial_state.commit(StateChangeCause::InitialState); - let trie_changes = initial_state.finalize().unwrap().1; + let trie_changes = initial_state.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); store_update.commit().unwrap(); @@ -865,7 +865,7 @@ fn test_delete_key_underflow() { initial_account_state.set_storage_usage(10); set_account(&mut state_update, alice_account(), &initial_account_state); state_update.commit(StateChangeCause::InitialState); - let trie_changes = state_update.finalize().unwrap().1; + let trie_changes = state_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); store_update.commit().unwrap(); diff --git a/runtime/runtime/src/tests/mod.rs b/runtime/runtime/src/tests/mod.rs index 43fae9547d1..c31cb26f0ce 100644 --- a/runtime/runtime/src/tests/mod.rs +++ b/runtime/runtime/src/tests/mod.rs @@ -58,7 +58,7 @@ fn test_get_account_from_trie() { let account_id = bob_account(); set_account(&mut state_update, account_id.clone(), &test_account); state_update.commit(StateChangeCause::InitialState); - let trie_changes = state_update.finalize().unwrap().1; + let trie_changes = state_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); store_update.commit().unwrap(); diff --git a/runtime/runtime/src/verifier.rs b/runtime/runtime/src/verifier.rs index 8930b78a10a..893d267f120 100644 --- a/runtime/runtime/src/verifier.rs +++ b/runtime/runtime/src/verifier.rs @@ -707,7 +707,7 @@ mod tests { set_account(&mut initial_state, account_id.clone(), &initial_account); } initial_state.commit(StateChangeCause::InitialState); - let trie_changes = initial_state.finalize().unwrap().1; + let trie_changes = initial_state.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); store_update.commit().unwrap(); diff --git a/tools/protocol-schema-check/res/protocol_schema.toml b/tools/protocol-schema-check/res/protocol_schema.toml index fadc9d2677a..1dbc2f29e2d 100644 --- a/tools/protocol-schema-check/res/protocol_schema.toml +++ b/tools/protocol-schema-check/res/protocol_schema.toml @@ -49,6 +49,9 @@ BufferedReceiptIndices = 2030010377 CachedParts = 1180507252 Challenge = 1405752277 ChallengeBody = 1534797173 +ChunkContractAccesses = 4097831706 +ChunkContractAccessesInner = 2563086819 +ChunkContractAccessesV1 = 2405344532 ChunkEndorsement = 1294072929 ChunkEndorsementInner = 2425301775 ChunkEndorsementMetadata = 1740861942 @@ -58,19 +61,29 @@ ChunkEndorsementsBitmap = 3112808654 ChunkExtraV1 = 774877102 ChunkHash = 1471814478 ChunkHashHeight = 825215623 +ChunkProductionKey = 2508733236 ChunkProofs = 4137471448 ChunkState = 1711319530 ChunkStateTransition = 307448170 ChunkStateWitness = 1612171766 ChunkStateWitnessAck = 177881908 ChunkStats = 4176245277 +CodeBytes = 3175376477 +CodeHash = 457384689 CompilationError = 738158707 +CompressedContractCode = 2821526605 CompressedEpochSyncProof = 1117061636 CongestionInfo = 2682682461 CongestionInfoV1 = 2571332168 ConnectionInfoRepr = 3621760869 ConsolidatedStateChange = 2252320929 ContractCacheKey = 1745279861 +ContractCodeRequest = 3970406885 +ContractCodeRequestInner = 4240547075 +ContractCodeRequestV1 = 1001986906 +ContractCodeResponse = 2106002570 +ContractCodeResponseInner = 3666251007 +ContractCodeResponseV1 = 3376811151 CreateAccountAction = 985240579 CryptoHash = 3799414537 CurrentEpochValidatorInfo = 2697567328 @@ -154,7 +167,7 @@ PeerChainInfoV2 = 1260985250 PeerId = 2447445523 PeerIdOrHash = 4080492546 PeerInfo = 3831734408 -PeerMessage = 3439156563 +PeerMessage = 265761395 Ping = 2783493472 Pong = 3159638327 PrepareError = 4009037507 @@ -179,8 +192,8 @@ ReceiptV1 = 2994842769 ReceiptValidationError = 551721215 ReceivedData = 3601438283 RootProof = 3135729669 -RoutedMessage = 81607980 -RoutedMessageBody = 1160524458 +RoutedMessage = 3486397151 +RoutedMessageBody = 185070333 RoutingTableUpdate = 2987752645 Secp256K1PublicKey = 4117078281 Secp256K1Signature = 3687154735 @@ -229,7 +242,8 @@ StateStoredReceiptMetadata = 2895538362 StateStoredReceiptV0 = 4029868827 StateSyncDumpProgress = 2225888613 StorageError = 2572184728 -StoredChunkStateTransitionData = 516372819 +StoredChunkStateTransitionData = 4079617051 +StoredChunkStateTransitionDataV1 = 1538767899 String = 2587724713 SyncSnapshotHosts = 1436852332 Tip = 305642482 diff --git a/tools/state-viewer/src/congestion_control.rs b/tools/state-viewer/src/congestion_control.rs index 02f5b842bfa..e0d8c46b5dd 100644 --- a/tools/state-viewer/src/congestion_control.rs +++ b/tools/state-viewer/src/congestion_control.rs @@ -207,7 +207,7 @@ impl PrepareBenchmarkCmd { } trie_update.commit(StateChangeCause::UpdatedDelayedReceipts); - let (_, trie_changes, _) = trie_update.finalize().unwrap(); + let trie_changes = trie_update.finalize().unwrap().trie_changes; let mut store_update = tries.store_update(); let new_state_root = tries.apply_all(&trie_changes, shard_uid, &mut store_update);