From 28d1fefadc9191c07ca2390f0d9da59d0d9f6f9c Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 28 Dec 2021 21:16:03 +0100 Subject: [PATCH] runtime-api: add validation_code_hash API (#4629) This is the first step to close https://github.com/paritytech/polkadot/issues/4524 --- node/core/runtime-api/src/cache.rs | 22 +++++++ node/core/runtime-api/src/lib.rs | 8 +++ node/core/runtime-api/src/tests.rs | 57 +++++++++++++++++++ node/subsystem-types/src/messages.rs | 7 +++ node/subsystem-util/src/lib.rs | 2 + primitives/src/v2/mod.rs | 6 ++ .../src/runtime-api/validation-code.md | 8 +++ runtime/kusama/src/lib.rs | 6 ++ runtime/parachains/src/runtime_api_impl/v1.rs | 13 +++++ runtime/polkadot/src/lib.rs | 6 ++ runtime/rococo/src/lib.rs | 6 ++ runtime/test-runtime/src/lib.rs | 6 ++ runtime/westend/src/lib.rs | 6 ++ 13 files changed, 153 insertions(+) diff --git a/node/core/runtime-api/src/cache.rs b/node/core/runtime-api/src/cache.rs index 1ba246fa1514..72206da8a114 100644 --- a/node/core/runtime-api/src/cache.rs +++ b/node/core/runtime-api/src/cache.rs @@ -48,6 +48,7 @@ const INBOUND_HRMP_CHANNELS_CACHE_SIZE: usize = 64 * 1024; const CURRENT_BABE_EPOCH_CACHE_SIZE: usize = 64 * 1024; const ON_CHAIN_VOTES_CACHE_SIZE: usize = 3 * 1024; const PVFS_REQUIRE_PRECHECK_SIZE: usize = 1024; +const VALIDATION_CODE_HASH_CACHE_SIZE: usize = 64 * 1024; struct ResidentSizeOf(T); @@ -111,6 +112,10 @@ pub(crate) struct RequestResultCache { current_babe_epoch: MemoryLruCache>, on_chain_votes: MemoryLruCache>>, pvfs_require_precheck: MemoryLruCache>>, + validation_code_hash: MemoryLruCache< + (Hash, ParaId, OccupiedCoreAssumption), + ResidentSizeOf>, + >, } impl Default for RequestResultCache { @@ -136,6 +141,7 @@ impl Default for RequestResultCache { current_babe_epoch: MemoryLruCache::new(CURRENT_BABE_EPOCH_CACHE_SIZE), on_chain_votes: MemoryLruCache::new(ON_CHAIN_VOTES_CACHE_SIZE), pvfs_require_precheck: MemoryLruCache::new(PVFS_REQUIRE_PRECHECK_SIZE), + validation_code_hash: MemoryLruCache::new(VALIDATION_CODE_HASH_CACHE_SIZE), } } } @@ -381,6 +387,21 @@ impl RequestResultCache { ) { self.pvfs_require_precheck.insert(relay_parent, ResidentSizeOf(pvfs)) } + + pub(crate) fn validation_code_hash( + &mut self, + key: (Hash, ParaId, OccupiedCoreAssumption), + ) -> Option<&Option> { + self.validation_code_hash.get(&key).map(|v| &v.0) + } + + pub(crate) fn cache_validation_code_hash( + &mut self, + key: (Hash, ParaId, OccupiedCoreAssumption), + value: Option, + ) { + self.validation_code_hash.insert(key, ResidentSizeOf(value)); + } } pub(crate) enum RequestResult { @@ -414,4 +435,5 @@ pub(crate) enum RequestResult { PvfsRequirePrecheck(Hash, Vec), // This is a request with side-effects and no result, hence (). SubmitPvfCheckStatement(Hash, PvfCheckStatement, ValidatorSignature, ()), + ValidationCodeHash(Hash, ParaId, OccupiedCoreAssumption, Option), } diff --git a/node/core/runtime-api/src/lib.rs b/node/core/runtime-api/src/lib.rs index 019705c530d6..95202eee9b31 100644 --- a/node/core/runtime-api/src/lib.rs +++ b/node/core/runtime-api/src/lib.rs @@ -160,6 +160,9 @@ where PvfsRequirePrecheck(relay_parent, pvfs) => self.requests_cache.cache_pvfs_require_precheck(relay_parent, pvfs), SubmitPvfCheckStatement(_, _, _, ()) => {}, + ValidationCodeHash(relay_parent, para_id, assumption, hash) => self + .requests_cache + .cache_validation_code_hash((relay_parent, para_id, assumption), hash), } } @@ -249,6 +252,9 @@ where // This request is side-effecting and thus cannot be cached. Some(request) }, + Request::ValidationCodeHash(para, assumption, sender) => + query!(validation_code_hash(para, assumption), sender) + .map(|sender| Request::ValidationCodeHash(para, assumption, sender)), } } @@ -486,6 +492,8 @@ where Request::PvfsRequirePrecheck(sender) => { query!(PvfsRequirePrecheck, pvfs_require_precheck(), ver = 2, sender) }, + Request::ValidationCodeHash(para, assumption, sender) => + query!(ValidationCodeHash, validation_code_hash(para, assumption), ver = 2, sender), } } diff --git a/node/core/runtime-api/src/tests.rs b/node/core/runtime-api/src/tests.rs index b9cb5f822657..d9e5700ff691 100644 --- a/node/core/runtime-api/src/tests.rs +++ b/node/core/runtime-api/src/tests.rs @@ -56,6 +56,7 @@ struct MockRuntimeApi { on_chain_votes: Option, submitted_pvf_check_statement: Arc>>, pvfs_require_precheck: Vec, + validation_code_hash: HashMap, } impl ProvideRuntimeApi for MockRuntimeApi { @@ -183,6 +184,14 @@ sp_api::mock_impl_runtime_apis! { fn pvfs_require_precheck() -> Vec { self.pvfs_require_precheck.clone() } + + fn validation_code_hash( + &self, + para: ParaId, + _assumption: OccupiedCoreAssumption, + ) -> Option { + self.validation_code_hash.get(¶).map(|c| c.clone()) + } } impl BabeApi for MockRuntimeApi { @@ -987,3 +996,51 @@ fn requests_pvfs_require_precheck() { futures::executor::block_on(future::join(subsystem_task, test_task)); } + +#[test] +fn requests_validation_code_hash() { + let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); + + let relay_parent = [1; 32].into(); + let para_a = 5.into(); + let para_b = 6.into(); + let spawner = sp_core::testing::TaskExecutor::new(); + let validation_code_hash = dummy_validation_code().hash(); + + let mut runtime_api = MockRuntimeApi::default(); + runtime_api.validation_code_hash.insert(para_a, validation_code_hash.clone()); + let runtime_api = Arc::new(runtime_api); + + let subsystem = RuntimeApiSubsystem::new(runtime_api.clone(), Metrics(None), spawner); + 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::ValidationCodeHash(para_a, OccupiedCoreAssumption::Included, tx), + ), + }) + .await; + + assert_eq!(rx.await.unwrap().unwrap(), Some(validation_code_hash)); + + let (tx, rx) = oneshot::channel(); + ctx_handle + .send(FromOverseer::Communication { + msg: RuntimeApiMessage::Request( + relay_parent, + Request::ValidationCodeHash(para_b, OccupiedCoreAssumption::Included, tx), + ), + }) + .await; + + assert_eq!(rx.await.unwrap().unwrap(), None); + + ctx_handle.send(FromOverseer::Signal(OverseerSignal::Conclude)).await; + }; + + futures::executor::block_on(future::join(subsystem_task, test_task)); +} diff --git a/node/subsystem-types/src/messages.rs b/node/subsystem-types/src/messages.rs index ecff4b2b1dd9..8a1ccd7af00d 100644 --- a/node/subsystem-types/src/messages.rs +++ b/node/subsystem-types/src/messages.rs @@ -673,6 +673,13 @@ pub enum RuntimeApiRequest { SubmitPvfCheckStatement(PvfCheckStatement, ValidatorSignature, RuntimeApiSender<()>), /// Returns code hashes of PVFs that require pre-checking by validators in the active set. PvfsRequirePrecheck(RuntimeApiSender>), + /// Get the validation code used by the specified para, taking the given `OccupiedCoreAssumption`, which + /// will inform on how the validation data should be computed if the para currently occupies a core. + ValidationCodeHash( + ParaId, + OccupiedCoreAssumption, + RuntimeApiSender>, + ), } /// A message to the Runtime API subsystem. diff --git a/node/subsystem-util/src/lib.rs b/node/subsystem-util/src/lib.rs index 99f9119e4d77..bf120c945f02 100644 --- a/node/subsystem-util/src/lib.rs +++ b/node/subsystem-util/src/lib.rs @@ -215,6 +215,8 @@ specialize_requests! { fn request_candidate_pending_availability(para_id: ParaId) -> Option; CandidatePendingAvailability; fn request_candidate_events() -> Vec; CandidateEvents; fn request_session_info(index: SessionIndex) -> Option; SessionInfo; + fn request_validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) + -> Option; ValidationCodeHash; } /// From the given set of validators, find the first key we can sign with, if any. diff --git a/primitives/src/v2/mod.rs b/primitives/src/v2/mod.rs index 83d5f521f376..065d5cc3c057 100644 --- a/primitives/src/v2/mod.rs +++ b/primitives/src/v2/mod.rs @@ -222,5 +222,11 @@ sp_api::decl_runtime_apis! { /// /// NOTE: This function is only available since parachain host version 2. fn pvfs_require_precheck() -> Vec; + + /// Fetch the hash of the validation code used by a para, making the given `OccupiedCoreAssumption`. + /// + /// NOTE: This function is only available since parachain host version 2. + fn validation_code_hash(para_id: v1::Id, assumption: v1::OccupiedCoreAssumption) + -> Option; } } diff --git a/roadmap/implementers-guide/src/runtime-api/validation-code.md b/roadmap/implementers-guide/src/runtime-api/validation-code.md index b39247570016..84c4e37a7376 100644 --- a/roadmap/implementers-guide/src/runtime-api/validation-code.md +++ b/roadmap/implementers-guide/src/runtime-api/validation-code.md @@ -11,3 +11,11 @@ Fetch the validation code (past, present or future) by its hash. ```rust fn validation_code_by_hash(at: Block, ValidationCodeHash) -> Option; ``` + +Fetch the validation code hash used by a para, making the given `OccupiedCoreAssumption`. + +> ⚠️ This API was introduced in `ParachainHost` v2. + +```rust +fn validation_code_hash(at: Block, ParaId, OccupiedCoreAssumption) -> Option; +``` diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index db55c0278c31..6a3710300b8e 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1830,6 +1830,12 @@ sp_api::impl_runtime_apis! { fn pvfs_require_precheck() -> Vec { parachains_runtime_api_impl::pvfs_require_precheck::() } + + fn validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) + -> Option + { + parachains_runtime_api_impl::validation_code_hash::(para_id, assumption) + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/runtime/parachains/src/runtime_api_impl/v1.rs b/runtime/parachains/src/runtime_api_impl/v1.rs index 480429bc547e..994a720bc590 100644 --- a/runtime/parachains/src/runtime_api_impl/v1.rs +++ b/runtime/parachains/src/runtime_api_impl/v1.rs @@ -389,3 +389,16 @@ pub fn submit_pvf_check_statement( pub fn pvfs_require_precheck() -> Vec { >::pvfs_require_precheck() } + +/// Returns the validation code hash for the given parachain making the given `OccupiedCoreAssumption`. +pub fn validation_code_hash( + para_id: ParaId, + assumption: OccupiedCoreAssumption, +) -> Option +where + T: inclusion::Config, +{ + with_assumption::(para_id, assumption, || { + >::current_code_hash(¶_id) + }) +} diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 5851b2027af4..d15ffcb04f3e 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -1797,6 +1797,12 @@ sp_api::impl_runtime_apis! { fn pvfs_require_precheck() -> Vec { parachains_runtime_api_impl::pvfs_require_precheck::() } + + fn validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) + -> Option + { + parachains_runtime_api_impl::validation_code_hash::(para_id, assumption) + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 72a3929706db..502060a11df2 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -1383,6 +1383,12 @@ sp_api::impl_runtime_apis! { fn pvfs_require_precheck() -> Vec { runtime_api_impl::pvfs_require_precheck::() } + + fn validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) + -> Option + { + runtime_api_impl::validation_code_hash::(para_id, assumption) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 217ca8146da4..2980e45e529e 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -888,6 +888,12 @@ sp_api::impl_runtime_apis! { fn pvfs_require_precheck() -> Vec { runtime_impl::pvfs_require_precheck::() } + + fn validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) + -> Option + { + runtime_impl::validation_code_hash::(para_id, assumption) + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 4a0494c11ec7..91bba4262c20 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1401,6 +1401,12 @@ sp_api::impl_runtime_apis! { fn pvfs_require_precheck() -> Vec { parachains_runtime_api_impl::pvfs_require_precheck::() } + + fn validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) + -> Option + { + parachains_runtime_api_impl::validation_code_hash::(para_id, assumption) + } } impl beefy_primitives::BeefyApi for Runtime {