Unverified Commit b8fc1acb authored by asynchronous rob's avatar asynchronous rob Committed by GitHub
Browse files

Runtime API for historical validation code (#1893)



* fix: ensure candidate validation gets code based on occupied core assumption

* guide: runtime API for historical validation code

* add historical runtime API

* integrate into runtime API subsystem

* remove blocked TODO

* fix service build: enable notifications protocol only under real overseer

* Update node/subsystem/src/messages.rs
Co-authored-by: Sergey Pepyakin's avatarSergei Shulepov <sergei@parity.io>

* fix compilation
Co-authored-by: default avatarRobert Habermeier <robert@Roberts-MacBook-Pro.local>
Co-authored-by: Sergey Pepyakin's avatarSergei Shulepov <sergei@parity.io>
parent 6447cb59
Pipeline #112976 passed with stages
in 23 minutes and 59 seconds
......@@ -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);
......
......@@ -123,6 +123,8 @@ fn make_runtime_api_request<Client>(
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<ParaId, ValidationData>,
session_index_for_child: SessionIndex,
validation_code: HashMap<ParaId, ValidationCode>,
historical_validation_code: HashMap<ParaId, Vec<(BlockNumber, ValidationCode)>>,
validation_outputs_results: HashMap<ParaId, bool>,
candidate_pending_availability: HashMap<ParaId, CommittedCandidateReceipt>,
candidate_events: Vec<CandidateEvent>,
......@@ -271,6 +275,19 @@ mod tests {
self.validation_code.get(&para).map(|c| c.clone())
}
fn historical_validation_code(
&self,
para: ParaId,
at: BlockNumber,
) -> Option<ValidationCode> {
self.historical_validation_code.get(&para).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));
}
}
......@@ -424,6 +424,16 @@ pub enum RuntimeApiRequest {
OccupiedCoreAssumption,
RuntimeApiSender<Option<ValidationCode>>,
),
/// 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<Option<ValidationCode>>,
),
/// Get a the candidate pending availability for a particular parachain by parachain / core index
CandidatePendingAvailability(ParaId, RuntimeApiSender<Option<CommittedCandidateReceipt>>),
/// Get all events concerning candidates (backing, inclusion, time-out) in the parent of
......
......@@ -657,7 +657,7 @@ pub enum CandidateEvent<H = Hash> {
sp_api::decl_runtime_apis! {
/// The API for querying the state of parachains on-chain.
pub trait ParachainHost<H: Decode = Hash, N: Decode = BlockNumber> {
pub trait ParachainHost<H: Decode = Hash, N: Encode + Decode = BlockNumber> {
/// Get the current validators.
fn validators() -> Vec<ValidatorId>;
......@@ -701,6 +701,14 @@ sp_api::decl_runtime_apis! {
fn validation_code(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<ValidationCode>;
/// 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<ValidationCode>;
/// 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<CommittedCandidateReceipt<H>>;
......
......@@ -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
......
# 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<ValidationCode>;
```
......@@ -409,7 +409,9 @@ enum RuntimeApiRequest {
SessionIndex(ResponseChannel<SessionIndex>),
/// Get the validation code for a specific para, using the given occupied core assumption.
ValidationCode(ParaId, OccupiedCoreAssumption, ResponseChannel<Option<ValidationCode>>),
/// 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<Option<ValidationCode>>),
/// with the given occupied core assumption.
PersistedValidationData(
ParaId,
......
......@@ -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<ValidationCode> {
None
}
fn candidate_pending_availability(_: Id) -> Option<CommittedCandidateReceipt<Hash>> {
None
}
......
......@@ -250,6 +250,14 @@ pub fn validation_code<T: initializer::Trait>(
)
}
/// Implementation for the `historical_validation_code` function of the runtime API.
pub fn historical_validation_code<T: initializer::Trait>(
para_id: ParaId,
context_height: T::BlockNumber,
) -> Option<ValidationCode> {
<paras::Module<T>>::validation_code_at(para_id, context_height, None)
}
/// Implementation for the `candidate_pending_availability` function of the runtime API.
pub fn candidate_pending_availability<T: initializer::Trait>(para_id: ParaId)
-> Option<CommittedCandidateReceipt<T::Hash>>
......
......@@ -1109,6 +1109,10 @@ sp_api::impl_runtime_apis! {
None
}
fn historical_validation_code(_: Id, _: BlockNumber) -> Option<ValidationCode> {
None
}
fn candidate_pending_availability(_: Id) -> Option<CommittedCandidateReceipt<Hash>> {
None
}
......
......@@ -657,6 +657,12 @@ sp_api::impl_runtime_apis! {
runtime_api_impl::validation_code::<Runtime>(para_id, assumption)
}
fn historical_validation_code(para_id: Id, context_height: BlockNumber)
-> Option<ValidationCode>
{
runtime_api_impl::historical_validation_code::<Runtime>(para_id, context_height)
}
fn candidate_pending_availability(para_id: Id) -> Option<CommittedCandidateReceipt<Hash>> {
runtime_api_impl::candidate_pending_availability::<Runtime>(para_id)
}
......
......@@ -646,6 +646,13 @@ sp_api::impl_runtime_apis! {
runtime_impl::validation_code::<Runtime>(para_id, assumption)
}
fn historical_validation_code(para_id: ParaId, context_height: BlockNumber)
-> Option<ValidationCode>
{
runtime_impl::historical_validation_code::<Runtime>(para_id, context_height)
}
fn candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt<Hash>> {
runtime_impl::candidate_pending_availability::<Runtime>(para_id)
}
......
......@@ -821,6 +821,10 @@ sp_api::impl_runtime_apis! {
None
}
fn historical_validation_code(_: Id, _: BlockNumber) -> Option<ValidationCode> {
None
}
fn check_validation_outputs(
_: Id,
_: primitives::v1::ValidationOutputs
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment