diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 2e0c871366..9ac9fcf404 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -258,7 +258,7 @@ async fn check_assumption_validation_data( descriptor.relay_parent, RuntimeApiRequest::ValidationCode( descriptor.para_id, - OccupiedCoreAssumption::Included, + assumption, code_tx, ), code_rx, @@ -648,7 +648,7 @@ mod tests { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( rp, - RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::Included, tx) + RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::TimedOut, tx) )) => { assert_eq!(rp, relay_parent); assert_eq!(p, para_id); @@ -756,7 +756,7 @@ mod tests { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( rp, - RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::Included, tx) + RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::TimedOut, tx) )) => { assert_eq!(rp, relay_parent); assert_eq!(p, para_id); diff --git a/polkadot/node/core/runtime-api/src/lib.rs b/polkadot/node/core/runtime-api/src/lib.rs index 9edc227757..cd0719ccb7 100644 --- a/polkadot/node/core/runtime-api/src/lib.rs +++ b/polkadot/node/core/runtime-api/src/lib.rs @@ -123,6 +123,8 @@ fn make_runtime_api_request( Request::SessionIndexForChild(sender) => query!(session_index_for_child(), sender), Request::ValidationCode(para, assumption, sender) => query!(validation_code(para, assumption), sender), + Request::HistoricalValidationCode(para, at, sender) => + query!(historical_validation_code(para, at), sender), Request::CandidatePendingAvailability(para, sender) => query!(candidate_pending_availability(para), sender), Request::CandidateEvents(sender) => query!(candidate_events(), sender), @@ -178,6 +180,7 @@ mod tests { ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, PersistedValidationData, Id as ParaId, OccupiedCoreAssumption, ValidationData, SessionIndex, ValidationCode, CommittedCandidateReceipt, CandidateEvent, AuthorityDiscoveryId, InboundDownwardMessage, + BlockNumber, }; use polkadot_node_subsystem_test_helpers as test_helpers; use sp_core::testing::TaskExecutor; @@ -193,6 +196,7 @@ mod tests { validation_data: HashMap, session_index_for_child: SessionIndex, validation_code: HashMap, + historical_validation_code: HashMap>, validation_outputs_results: HashMap, candidate_pending_availability: HashMap, candidate_events: Vec, @@ -271,6 +275,19 @@ mod tests { self.validation_code.get(¶).map(|c| c.clone()) } + fn historical_validation_code( + &self, + para: ParaId, + at: BlockNumber, + ) -> Option { + self.historical_validation_code.get(¶).and_then(|h_code| { + h_code.iter() + .take_while(|(changed_at, _)| changed_at <= &at) + .last() + .map(|(_, code)| code.clone()) + }) + } + fn candidate_pending_availability( &self, para: ParaId, @@ -684,4 +701,72 @@ mod tests { futures::executor::block_on(future::join(subsystem_task, test_task)); } + #[test] + fn requests_historical_code() { + let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new()); + + let para_a = 5.into(); + let para_b = 6.into(); + + let runtime_api = Arc::new({ + let mut runtime_api = MockRuntimeApi::default(); + + runtime_api.historical_validation_code.insert( + para_a, + vec![(1, vec![1, 2, 3].into()), (10, vec![4, 5, 6].into())], + ); + + runtime_api.historical_validation_code.insert( + para_b, + vec![(5, vec![7, 8, 9].into())], + ); + + runtime_api + }); + let relay_parent = [1; 32].into(); + + let subsystem = RuntimeApiSubsystem::new(runtime_api, Metrics(None)); + let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); + let test_task = async move { + { + let (tx, rx) = oneshot::channel(); + ctx_handle.send(FromOverseer::Communication { + msg: RuntimeApiMessage::Request( + relay_parent, + Request::HistoricalValidationCode(para_a, 5, tx), + ) + }).await; + + assert_eq!(rx.await.unwrap().unwrap(), Some(ValidationCode::from(vec![1, 2, 3]))); + } + + { + let (tx, rx) = oneshot::channel(); + ctx_handle.send(FromOverseer::Communication { + msg: RuntimeApiMessage::Request( + relay_parent, + Request::HistoricalValidationCode(para_a, 10, tx), + ) + }).await; + + assert_eq!(rx.await.unwrap().unwrap(), Some(ValidationCode::from(vec![4, 5, 6]))); + } + + { + let (tx, rx) = oneshot::channel(); + ctx_handle.send(FromOverseer::Communication { + msg: RuntimeApiMessage::Request( + relay_parent, + Request::HistoricalValidationCode(para_b, 1, tx), + ) + }).await; + + assert!(rx.await.unwrap().unwrap().is_none()); + } + + ctx_handle.send(FromOverseer::Signal(OverseerSignal::Conclude)).await; + }; + + futures::executor::block_on(future::join(subsystem_task, test_task)); + } } diff --git a/polkadot/node/subsystem/src/messages.rs b/polkadot/node/subsystem/src/messages.rs index 425debfd12..eddae49b8d 100644 --- a/polkadot/node/subsystem/src/messages.rs +++ b/polkadot/node/subsystem/src/messages.rs @@ -424,6 +424,16 @@ pub enum RuntimeApiRequest { OccupiedCoreAssumption, RuntimeApiSender>, ), + /// Fetch the historical validation code used by a para for candidates executed in the + /// context of a given block height in the current chain. + /// + /// `context_height` may be no greater than the height of the block in whose + /// state the runtime API is executed. Otherwise `None` is returned. + HistoricalValidationCode( + ParaId, + BlockNumber, + RuntimeApiSender>, + ), /// Get a the candidate pending availability for a particular parachain by parachain / core index CandidatePendingAvailability(ParaId, RuntimeApiSender>), /// Get all events concerning candidates (backing, inclusion, time-out) in the parent of diff --git a/polkadot/primitives/src/v1.rs b/polkadot/primitives/src/v1.rs index 5347849f7f..33cf290934 100644 --- a/polkadot/primitives/src/v1.rs +++ b/polkadot/primitives/src/v1.rs @@ -657,7 +657,7 @@ pub enum CandidateEvent { sp_api::decl_runtime_apis! { /// The API for querying the state of parachains on-chain. - pub trait ParachainHost { + pub trait ParachainHost { /// Get the current validators. fn validators() -> Vec; @@ -701,6 +701,14 @@ sp_api::decl_runtime_apis! { fn validation_code(para_id: Id, assumption: OccupiedCoreAssumption) -> Option; + /// Fetch the historical validation code used by a para for candidates executed in the + /// context of a given block height in the current chain. + /// + /// `context_height` may be no greater than the height of the block in whose + /// state the runtime API is executed. + fn historical_validation_code(para_id: Id, context_height: N) + -> Option; + /// Get the receipt of a candidate pending availability. This returns `Some` for any paras /// assigned to occupied cores in `availability_cores` and `None` otherwise. fn candidate_pending_availability(para_id: Id) -> Option>; diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md index 4bafdd9e7e..72bb9d075c 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md @@ -232,7 +232,7 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`: #### `launch_approval(SessionIndex, CandidateDescriptor, ValidatorIndex, block_hash, candidate_index)`: * Extract the public key of the `ValidatorIndex` from the `SessionInfo` for the session. * Issue an `AvailabilityRecoveryMessage::RecoverAvailableData(candidate, session_index, response_sender)` - * Load the historical validation code of the parachain (TODO: https://github.com/paritytech/polkadot/issues/1877) + * Load the historical validation code of the parachain by dispatching a `RuntimeApiRequest::HistoricalValidationCode(`descriptor.para_id`, `descriptor.relay_parent`)` against the state of `block_hash`. * Spawn a background task with a clone of `approval_vote_tx` * Wait for the available data * Issue a `CandidateValidationMessage::ValidateFromExhaustive` message diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/historical_validation_code.md b/polkadot/roadmap/implementers-guide/src/runtime-api/historical_validation_code.md new file mode 100644 index 0000000000..0b63f247df --- /dev/null +++ b/polkadot/roadmap/implementers-guide/src/runtime-api/historical_validation_code.md @@ -0,0 +1,7 @@ +# Historical Validation Code + +Fetch the historical validation code used by a para for candidates executed in the context of a given block height in the current chain. + +```rust +fn historical_validation_code(at: Block, para_id: ParaId, context_height: BlockNumber) -> Option; +``` diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index a6561ca661..484d53b70f 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -409,7 +409,9 @@ enum RuntimeApiRequest { SessionIndex(ResponseChannel), /// Get the validation code for a specific para, using the given occupied core assumption. ValidationCode(ParaId, OccupiedCoreAssumption, ResponseChannel>), - /// Get the persisted validation data at the state of a given block for a specific para, + /// Fetch the historical validation code used by a para for candidates executed in + /// the context of a given block height in the current chain. + HistoricalValidationCode(ParaId, BlockNumber, ResponseChannel>), /// with the given occupied core assumption. PersistedValidationData( ParaId, diff --git a/polkadot/runtime/kusama/src/lib.rs b/polkadot/runtime/kusama/src/lib.rs index 1b7e3fbb17..26a2ddb92e 100644 --- a/polkadot/runtime/kusama/src/lib.rs +++ b/polkadot/runtime/kusama/src/lib.rs @@ -89,7 +89,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("kusama"), impl_name: create_runtime_str!("parity-kusama"), authoring_version: 2, - spec_version: 2026, + spec_version: 2027, impl_version: 0, #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, @@ -1115,6 +1115,10 @@ sp_api::impl_runtime_apis! { None } + fn historical_validation_code(_: Id, _: BlockNumber) -> Option { + None + } + fn candidate_pending_availability(_: Id) -> Option> { None } diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs index 5eb6ac6026..5d9a36fa73 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs @@ -250,6 +250,14 @@ pub fn validation_code( ) } +/// Implementation for the `historical_validation_code` function of the runtime API. +pub fn historical_validation_code( + para_id: ParaId, + context_height: T::BlockNumber, +) -> Option { + >::validation_code_at(para_id, context_height, None) +} + /// Implementation for the `candidate_pending_availability` function of the runtime API. pub fn candidate_pending_availability(para_id: ParaId) -> Option> diff --git a/polkadot/runtime/polkadot/src/lib.rs b/polkadot/runtime/polkadot/src/lib.rs index 92bfcd1ff8..4aa91c0841 100644 --- a/polkadot/runtime/polkadot/src/lib.rs +++ b/polkadot/runtime/polkadot/src/lib.rs @@ -1109,6 +1109,10 @@ sp_api::impl_runtime_apis! { None } + fn historical_validation_code(_: Id, _: BlockNumber) -> Option { + None + } + fn candidate_pending_availability(_: Id) -> Option> { None } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 213d5bbeca..1e4e0ae82c 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -657,6 +657,12 @@ sp_api::impl_runtime_apis! { runtime_api_impl::validation_code::(para_id, assumption) } + fn historical_validation_code(para_id: Id, context_height: BlockNumber) + -> Option + { + runtime_api_impl::historical_validation_code::(para_id, context_height) + } + fn candidate_pending_availability(para_id: Id) -> Option> { runtime_api_impl::candidate_pending_availability::(para_id) } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 46140f0b63..0ed03f99a6 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -646,6 +646,13 @@ sp_api::impl_runtime_apis! { runtime_impl::validation_code::(para_id, assumption) } + fn historical_validation_code(para_id: ParaId, context_height: BlockNumber) + -> Option + { + runtime_impl::historical_validation_code::(para_id, context_height) + } + + fn candidate_pending_availability(para_id: ParaId) -> Option> { runtime_impl::candidate_pending_availability::(para_id) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 756a4b2672..ce28bba0c3 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -821,6 +821,10 @@ sp_api::impl_runtime_apis! { None } + fn historical_validation_code(_: Id, _: BlockNumber) -> Option { + None + } + fn check_validation_outputs( _: Id, _: primitives::v1::ValidationOutputs