From e76d50ba879962692564fa16ecff0f44b2cac4d6 Mon Sep 17 00:00:00 2001 From: girazoki Date: Wed, 13 Nov 2024 11:22:16 +0100 Subject: [PATCH 01/14] wip --- .../tanssi-relay-service/src/dev_service.rs | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index bd12767b2..fe543c7dd 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -42,7 +42,7 @@ use { polkadot_core_primitives::{AccountId, Balance, Block, Hash, Nonce}, polkadot_node_core_parachains_inherent::Error as InherentError, polkadot_overseer::Handle, - polkadot_primitives::InherentData as ParachainsInherentData, + polkadot_primitives::{InherentData as ParachainsInherentData, runtime_api::ParachainHost}, polkadot_rpc::{DenyUnsafe, RpcExtension}, polkadot_service::{ BlockT, Error, IdentifyVariant, NewFullParams, OverseerGen, SelectRelayChain, @@ -54,12 +54,15 @@ use { run_manual_seal, EngineCommand, ManualSealParams, }, sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}, + sc_keystore::{LocalKeystore, Keystore}, sc_transaction_pool_api::{OffchainTransactionPoolFactory, TransactionPool}, service::{Configuration, KeystoreContainer, RpcHandlers, TaskManager}, sp_api::ProvideRuntimeApi, sp_block_builder::BlockBuilder, sp_blockchain::{HeaderBackend, HeaderMetadata}, sp_consensus_babe::SlotDuration, + sp_core::ByteArray, + sp_keystore::KeystorePtr, std::{cmp::max, ops::Add, sync::Arc, time::Duration}, telemetry::{Telemetry, TelemetryWorker, TelemetryWorkerHandle}, }; @@ -150,9 +153,10 @@ where /// We use EmptyParachainsInherentDataProvider to insert an empty parachain inherent in the block /// to satisfy runtime -struct EmptyParachainsInherentDataProvider> { +struct EmptyParachainsInherentDataProvider + ProvideRuntimeApi> { pub client: Arc, pub parent: Hash, + pub keystore: KeystorePtr } /// Copied from polkadot service just so that this code retains same structure as @@ -165,14 +169,17 @@ struct Basics { telemetry: Option, } -impl> EmptyParachainsInherentDataProvider { - pub fn new(client: Arc, parent: Hash) -> Self { - EmptyParachainsInherentDataProvider { client, parent } +impl + ProvideRuntimeApi> EmptyParachainsInherentDataProvider where +C::Api: ParachainHost +{ + pub fn new(client: Arc, parent: Hash, keystore: KeystorePtr) -> Self { + EmptyParachainsInherentDataProvider { client, parent, keystore } } pub async fn create( client: Arc, parent: Hash, + keystore: KeystorePtr ) -> Result { let parent_header = match client.header(parent) { Ok(Some(h)) => h, @@ -180,6 +187,43 @@ impl> EmptyParachainsInherentDataProvider { Err(err) => return Err(InherentError::Blockchain(err)), }; + // Strategy: + // we usually have 1 validator per core, and we usually run with --alice + // the idea is that at least alice will be assigned to one core + // if we find in the keystore the validator attached to a particular core, + // we generate a signature for the parachain assigned to that core + // To retrieve the validator keys, cal runtime api: + + // this following piece of code predicts whether the validator is assigned to a particular + // core where a candidate for a parachain needs to be created + let runtime_api = client.runtime_api(); + + let para_authorities = runtime_api.validators(parent).unwrap(); + let claim_queue = runtime_api.claim_queue(parent).unwrap(); + let (groups, rotation_info) = runtime_api.validator_groups(parent).unwrap(); + let rotations_since_session_start = + (parent_header.number - rotation_info.session_start_block) / rotation_info.group_rotation_frequency; + + // Get all the available keys + let available_keys = keystore.keys(polkadot_primitives::PARACHAIN_KEY_TYPE_ID).unwrap(); + + for (core, para) in claim_queue { + let group_assigned_to_core = core.0 + rotations_since_session_start % groups.len() as u32; + let indices_associated_to_core = groups.get(group_assigned_to_core as usize).unwrap(); + for index in indices_associated_to_core { + let validator_keys_to_find = para_authorities.get(index.0 as usize).unwrap(); + // Iterate keys until we find an eligible one, or run out of candidates. + for type_public_pair in &available_keys { + if let Ok(validator) = polkadot_primitives::ValidatorId::from_slice(&type_public_pair) { + if validator_keys_to_find == &validator { + // Only in this case, we need to create a candidate + log::info!("found public key"); + } + } + } + } + } + Ok(ParachainsInherentData { bitfields: Vec::new(), backed_candidates: Vec::new(), @@ -190,15 +234,17 @@ impl> EmptyParachainsInherentDataProvider { } #[async_trait::async_trait] -impl> sp_inherents::InherentDataProvider +impl + ProvideRuntimeApi> sp_inherents::InherentDataProvider for EmptyParachainsInherentDataProvider + + where C::Api: ParachainHost { async fn provide_inherent_data( &self, dst_inherent_data: &mut sp_inherents::InherentData, ) -> Result<(), sp_inherents::Error> { let inherent_data = - EmptyParachainsInherentDataProvider::create(self.client.clone(), self.parent) + EmptyParachainsInherentDataProvider::create(self.client.clone(), self.parent, self.keystore.clone()) .await .map_err(|e| sp_inherents::Error::Application(Box::new(e)))?; @@ -403,6 +449,7 @@ fn new_full< }, )), }; + let keystore_clone = keystore.clone(); let babe_config = babe_link.config(); let babe_consensus_provider = BabeConsensusDataProvider::new( @@ -418,6 +465,7 @@ fn new_full< // Need to clone it and store here to avoid moving of `client` // variable in closure below. let client_clone = client.clone(); + task_manager.spawn_essential_handle().spawn_blocking( "authorship_task", Some("block-authoring"), @@ -430,12 +478,13 @@ fn new_full< select_chain, create_inherent_data_providers: move |parent, ()| { let client_clone = client_clone.clone(); - + let keystore = keystore_clone.clone(); async move { let parachain = EmptyParachainsInherentDataProvider::new( client_clone.clone(), parent, + keystore ); let timestamp = get_next_timestamp(client_clone, slot_duration); From 9b39ff1e67b07cd42e04cb3cde9705962f8963c2 Mon Sep 17 00:00:00 2001 From: girazoki Date: Thu, 14 Nov 2024 12:34:54 +0100 Subject: [PATCH 02/14] wip --- Cargo.lock | 1 + .../node/tanssi-relay-service/Cargo.toml | 1 + .../tanssi-relay-service/src/dev_service.rs | 191 +++++++++++++++++- 3 files changed, 186 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b29662dc3..d3c4ca88b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17576,6 +17576,7 @@ dependencies = [ "sp-block-builder", "sp-blockchain", "sp-consensus", + "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-core", diff --git a/solo-chains/node/tanssi-relay-service/Cargo.toml b/solo-chains/node/tanssi-relay-service/Cargo.toml index 089e7c5e3..96d3c81a8 100644 --- a/solo-chains/node/tanssi-relay-service/Cargo.toml +++ b/solo-chains/node/tanssi-relay-service/Cargo.toml @@ -48,6 +48,7 @@ sp-authority-discovery = { workspace = true } sp-block-builder = { workspace = true } sp-blockchain = { workspace = true } sp-consensus-babe = { workspace = true } +sp-consensus-aura = { workspace = true } sp-core = { workspace = true, features = [ "std" ] } sp-inherents = { workspace = true, features = [ "std" ] } sp-io = { workspace = true, features = [ "std" ] } diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index fe543c7dd..f093eef23 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -159,6 +159,14 @@ struct EmptyParachainsInherentDataProvider + ProvideRunt pub keystore: KeystorePtr } +use sp_consensus_aura::{inherents::InherentType as AuraInherentType, AURA_ENGINE_ID}; +use sp_runtime::{traits::BlakeTwo256, DigestItem, RuntimeAppPublic}; +use polkadot_primitives::BackedCandidate; +use polkadot_primitives::OccupiedCoreAssumption; +use sp_core::H256; +use polkadot_primitives::CollatorPair; +use sp_core::Pair; +use polkadot_primitives::{CommittedCandidateReceipt, CandidateDescriptor, CandidateCommitments, CompactStatement, EncodeAs, SigningContext, ValidityAttestation}; /// Copied from polkadot service just so that this code retains same structure as /// polkadot_service crate. struct Basics { @@ -181,12 +189,19 @@ C::Api: ParachainHost parent: Hash, keystore: KeystorePtr ) -> Result { - let parent_header = match client.header(parent) { + let parent_header_relay = match client.header(parent) { Ok(Some(h)) => h, Ok(None) => return Err(InherentError::ParentHeaderNotFound(parent)), Err(err) => return Err(InherentError::Blockchain(err)), }; + let parent_hash = client.hash(parent_header_relay.number.saturating_sub(1)).unwrap().unwrap(); + + let parent_header = match client.header(parent_hash) { + Ok(Some(h)) => h, + Ok(None) => return Err(InherentError::ParentHeaderNotFound(parent)), + Err(err) => return Err(InherentError::Blockchain(err)), + }; // Strategy: // we usually have 1 validator per core, and we usually run with --alice // the idea is that at least alice will be assigned to one core @@ -198,15 +213,59 @@ C::Api: ParachainHost // core where a candidate for a parachain needs to be created let runtime_api = client.runtime_api(); - let para_authorities = runtime_api.validators(parent).unwrap(); - let claim_queue = runtime_api.claim_queue(parent).unwrap(); - let (groups, rotation_info) = runtime_api.validator_groups(parent).unwrap(); + let para_authorities = runtime_api.validators(parent_hash).unwrap(); + let claim_queue = runtime_api.claim_queue(parent_hash).unwrap(); + let (groups, rotation_info) = runtime_api.validator_groups(parent_hash).unwrap(); let rotations_since_session_start = (parent_header.number - rotation_info.session_start_block) / rotation_info.group_rotation_frequency; // Get all the available keys let available_keys = keystore.keys(polkadot_primitives::PARACHAIN_KEY_TYPE_ID).unwrap(); + let slot_number = AuraInherentType::from( + u64::from(parent_header.number), + ); + + let parachain_mocked_header = sp_runtime::generic::Header:: { + parent_hash: Default::default(), + number: parent_header.number, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: sp_runtime::generic::Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot_number.encode())], + } + }; + let availability_cores = runtime_api.availability_cores(parent_hash).unwrap(); + let session_idx = runtime_api.session_index_for_child(parent_hash).unwrap(); + let all_validators = runtime_api.validators(parent_hash).unwrap(); + let availability_bitvec = availability_bitvec(1, availability_cores.len()); + + + let signature_ctx = SigningContext { + parent_hash: parent, + session_index: session_idx, + }; + + log::info!("availability bitvec is {:?}", availability_bitvec); + // we generate the availability bitfield sigs + let bitfields: Vec> = all_validators + .iter() + .enumerate() + .map(|(i, public)| { + keystore_sign( + &keystore, + availability_bitvec.clone(), + &signature_ctx, + ValidatorIndex(i as u32), + &public + ).unwrap().unwrap() + }) + .collect(); + + log::info!("bitfields {:?}", bitfields); + + let collator_pair = CollatorPair::generate().0; + let mut backed_cand: Vec> = vec![]; for (core, para) in claim_queue { let group_assigned_to_core = core.0 + rotations_since_session_start % groups.len() as u32; let indices_associated_to_core = groups.get(group_assigned_to_core as usize).unwrap(); @@ -216,19 +275,95 @@ C::Api: ParachainHost for type_public_pair in &available_keys { if let Ok(validator) = polkadot_primitives::ValidatorId::from_slice(&type_public_pair) { if validator_keys_to_find == &validator { + let persisted_validation_data = runtime_api.persisted_validation_data(parent_hash, para[0], OccupiedCoreAssumption::Included).unwrap().unwrap(); + log::info!("parent_hash is {:?}", parent_hash); + + log::info!("validation data is {:?}", persisted_validation_data); + log::info!("validation data encoded is {:?}", persisted_validation_data.encode()); + log::info!("parent number is is {:?}", parent_header.number); + + let persisted_validation_data_hash = persisted_validation_data.hash(); + let validation_code_hash = runtime_api.validation_code_hash(parent_hash, para[0], OccupiedCoreAssumption::Included).unwrap().unwrap(); + let pov_hash = Default::default(); + let payload = + polkadot_primitives::collator_signature_payload( + &parent_hash, + ¶[0], + &persisted_validation_data_hash, + &pov_hash, + &validation_code_hash, + ); + let collator_signature = collator_pair.sign(&payload); + let prev_head = persisted_validation_data.parent_head; + let candidate = CommittedCandidateReceipt:: { + descriptor: CandidateDescriptor:: { + para_id: para[0], + relay_parent: parent_hash, + collator: collator_pair.public(), + persisted_validation_data_hash, + pov_hash, + erasure_root: Default::default(), + signature: collator_signature, + para_head: parachain_mocked_header.clone().hash(), + validation_code_hash, + }, + commitments: CandidateCommitments:: { + upward_messages: Default::default(), + horizontal_messages: Default::default(), + new_validation_code: None, + head_data: parachain_mocked_header.clone().encode().into(), + processed_downward_messages: 0, + hrmp_watermark: parent_header.number, + }, + }; + let candidate_hash = candidate.hash(); + let payload = CompactStatement::Valid(candidate_hash); + + let signature_ctx = SigningContext { + parent_hash: parent_hash, + session_index: session_idx, + }; + + log::info!("before sig"); + + let signature = keystore_sign( + &keystore, + payload, + &signature_ctx, + *index, + &validator + ).unwrap().unwrap().benchmark_signature(); + + log::info!("after sig"); + + let validity_votes = vec![ValidityAttestation::Explicit(signature)]; + + backed_cand.push(BackedCandidate::::new( + candidate, + validity_votes.clone(), + bitvec::bitvec![u8, bitvec::order::Lsb0; 1; indices_associated_to_core.len()], + Some(core), + )); + + + + // Only in this case, we need to create a candidate log::info!("found public key"); + log::info!("validity_votes {:?}", validity_votes.clone()); } } } } } + log::info!("backed_cand {:?}", backed_cand); + Ok(ParachainsInherentData { - bitfields: Vec::new(), - backed_candidates: Vec::new(), + bitfields: bitfields, + backed_candidates: backed_cand, disputes: Vec::new(), - parent_header, + parent_header: parent_header_relay, }) } } @@ -248,6 +383,7 @@ impl + ProvideRuntimeApi> sp_inherents::InherentD .await .map_err(|e| sp_inherents::Error::Application(Box::new(e)))?; + log::info!("inherent data {:?}", inherent_data); dst_inherent_data.put_data( polkadot_primitives::PARACHAINS_INHERENT_IDENTIFIER, &inherent_data, @@ -679,3 +815,44 @@ fn new_partial_basics( telemetry, }) } + +use polkadot_primitives::{ValidatorIndex, ValidatorId, UncheckedSigned, AvailabilityBitfield}; +use sp_keystore::Error as KeystoreError; +fn keystore_sign( + keystore: &KeystorePtr, + payload: Payload, + context: &SigningContext, + validator_index: ValidatorIndex, + key: &ValidatorId, +) -> Result>, KeystoreError> { + let data = payload_data(&payload, context); + let signature = + keystore.sr25519_sign(ValidatorId::ID, key.as_ref(), &data)?.map(|sig| UncheckedSigned::new( + payload, + validator_index, + sig.into(), + )); + Ok(signature) +} + +fn payload_data(payload: &Payload, context: &SigningContext) -> Vec { + // equivalent to (`real_payload`, context).encode() + let mut out = payload.encode_as(); + out.extend(context.encode()); + out +} + +/// Create an `AvailabilityBitfield` where `concluding` is a map where each key is a core index + /// that is concluding and `cores` is the total number of cores in the system. + fn availability_bitvec(used_cores: usize, cores: usize) -> AvailabilityBitfield { + let mut bitfields = bitvec::bitvec![u8, bitvec::order::Lsb0; 0; 0]; + for i in 0..cores { + if i < used_cores { + bitfields.push(true); + } else { + bitfields.push(false) + } + } + + bitfields.into() + } \ No newline at end of file From cae06bc3fdd3fb51f8056dadfde8de0bde0419bf Mon Sep 17 00:00:00 2001 From: girazoki Date: Thu, 14 Nov 2024 14:38:42 +0100 Subject: [PATCH 03/14] add flume for messages --- Cargo.lock | 1 + .../node/tanssi-relay-service/Cargo.toml | 1 + .../node/tanssi-relay-service/src/dev_rpcs.rs | 81 ++++ .../tanssi-relay-service/src/dev_service.rs | 377 ++++++++++++------ .../node/tanssi-relay-service/src/lib.rs | 2 + 5 files changed, 329 insertions(+), 133 deletions(-) create mode 100644 solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs diff --git a/Cargo.lock b/Cargo.lock index d3c4ca88b..1d9a65edf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17483,6 +17483,7 @@ dependencies = [ "dancelight-runtime-constants", "dp-container-chain-genesis-data", "env_logger 0.11.3", + "flume 0.10.14", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", diff --git a/solo-chains/node/tanssi-relay-service/Cargo.toml b/solo-chains/node/tanssi-relay-service/Cargo.toml index 96d3c81a8..76da71989 100644 --- a/solo-chains/node/tanssi-relay-service/Cargo.toml +++ b/solo-chains/node/tanssi-relay-service/Cargo.toml @@ -82,6 +82,7 @@ async-io = { workspace = true } async-trait = { workspace = true } bitvec = { workspace = true, optional = true } codec = { workspace = true } +flume = { workspace = true } futures = { workspace = true } gum = { workspace = true } hex-literal = { workspace = true } diff --git a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs new file mode 100644 index 000000000..5ccd24e5a --- /dev/null +++ b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs @@ -0,0 +1,81 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Tanssi is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Tanssi. If not, see + +//! Development Polkadot service. Adapted from `polkadot_service` crate +//! and removed un-necessary components which are not required in dev node. + +use codec::{Decode, Encode}; +use jsonrpsee::{ + core::RpcResult, + proc_macros::rpc, + types::{ + error::{INTERNAL_ERROR_CODE, INTERNAL_ERROR_MSG}, + ErrorObjectOwned, + }, +}; + +/// This RPC interface is used to provide methods in dev mode only +#[rpc(server)] +#[jsonrpsee::core::async_trait] +pub trait DevApi { + /// Indicate the mock parachain candidate insertion to be active + #[method(name = "mock_enableParaInherentCandidate")] + async fn enable_para_inherent_candidate(&self) -> RpcResult<()>; + + /// Indicate the mock parachain candidate insertion to be disabled + #[method(name = "mock_disableParaInherentCandidate")] + async fn disable_para_inherent_candidate(&self) -> RpcResult<()>; +} + +pub struct DevRpc { + pub mock_para_inherent_channel: flume::Sender>, +} + +#[jsonrpsee::core::async_trait] +impl DevApiServer for DevRpc { + async fn enable_para_inherent_candidate(&self) -> RpcResult<()> { + let mock_para_inherent_channel = self.mock_para_inherent_channel.clone(); + // Push the message to the shared channel where it will be queued up + // to be injected in to an upcoming block. + mock_para_inherent_channel + .send_async(true.encode()) + .await + .map_err(|err| internal_err(err.to_string()))?; + + Ok(()) + } + + async fn disable_para_inherent_candidate(&self) -> RpcResult<()> { + let mock_para_inherent_channel = self.mock_para_inherent_channel.clone(); + // Push the message to the shared channel where it will be queued up + // to be injected in to an upcoming block. + mock_para_inherent_channel + .send_async(false.encode()) + .await + .map_err(|err| internal_err(err.to_string()))?; + + Ok(()) + } +} + +// This bit cribbed from frontier. +pub fn internal_err(message: T) -> ErrorObjectOwned { + ErrorObjectOwned::owned( + INTERNAL_ERROR_CODE, + INTERNAL_ERROR_MSG, + Some(message.to_string()), + ) +} diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index f093eef23..c2dc8ac26 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -30,6 +30,8 @@ //! 10. If amount of time passed between two block is less than slot duration, we emulate passing of time babe block import and runtime //! by incrementing timestamp by slot duration. +use crate::dev_rpcs::DevRpc; + use { async_io::Timer, babe::{BabeBlockImport, BabeLink}, @@ -42,7 +44,7 @@ use { polkadot_core_primitives::{AccountId, Balance, Block, Hash, Nonce}, polkadot_node_core_parachains_inherent::Error as InherentError, polkadot_overseer::Handle, - polkadot_primitives::{InherentData as ParachainsInherentData, runtime_api::ParachainHost}, + polkadot_primitives::{runtime_api::ParachainHost, InherentData as ParachainsInherentData}, polkadot_rpc::{DenyUnsafe, RpcExtension}, polkadot_service::{ BlockT, Error, IdentifyVariant, NewFullParams, OverseerGen, SelectRelayChain, @@ -54,7 +56,7 @@ use { run_manual_seal, EngineCommand, ManualSealParams, }, sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}, - sc_keystore::{LocalKeystore, Keystore}, + sc_keystore::{Keystore, LocalKeystore}, sc_transaction_pool_api::{OffchainTransactionPoolFactory, TransactionPool}, service::{Configuration, KeystoreContainer, RpcHandlers, TaskManager}, sp_api::ProvideRuntimeApi, @@ -67,6 +69,10 @@ use { telemetry::{Telemetry, TelemetryWorker, TelemetryWorkerHandle}, }; +use crate::dev_rpcs::DevApiServer; + +const PARA_INHERENT_SELECTOR_AUX_KEY: &[u8] = b"__DEV_PARA_INHERENT_SELECTOR"; + pub type FullBackend = service::TFullBackend; pub type FullClient = service::TFullClient< @@ -100,6 +106,8 @@ struct DevDeps { pub deny_unsafe: DenyUnsafe, /// Manual seal command sink pub command_sink: Option>>, + /// Channels for dev rpcs + pub dev_rpc_data: Option>>, } fn create_dev_rpc_extension( @@ -109,6 +117,7 @@ fn create_dev_rpc_extension( chain_spec, deny_unsafe, command_sink: maybe_command_sink, + dev_rpc_data: maybe_dev_rpc_data, }: DevDeps, ) -> Result> where @@ -148,25 +157,36 @@ where io.merge(ManualSeal::new(command_sink).into_rpc())?; } + if let Some(mock_para_inherent_channel) = maybe_dev_rpc_data { + io.merge( + DevRpc { + mock_para_inherent_channel, + } + .into_rpc(), + )?; + } + Ok(io) } /// We use EmptyParachainsInherentDataProvider to insert an empty parachain inherent in the block /// to satisfy runtime -struct EmptyParachainsInherentDataProvider + ProvideRuntimeApi> { +struct EmptyParachainsInherentDataProvider> { pub client: Arc, pub parent: Hash, - pub keystore: KeystorePtr } -use sp_consensus_aura::{inherents::InherentType as AuraInherentType, AURA_ENGINE_ID}; -use sp_runtime::{traits::BlakeTwo256, DigestItem, RuntimeAppPublic}; use polkadot_primitives::BackedCandidate; -use polkadot_primitives::OccupiedCoreAssumption; -use sp_core::H256; use polkadot_primitives::CollatorPair; +use polkadot_primitives::OccupiedCoreAssumption; +use polkadot_primitives::{ + CandidateCommitments, CandidateDescriptor, CommittedCandidateReceipt, CompactStatement, + EncodeAs, SigningContext, ValidityAttestation, +}; +use sp_consensus_aura::{inherents::InherentType as AuraInherentType, AURA_ENGINE_ID}; use sp_core::Pair; -use polkadot_primitives::{CommittedCandidateReceipt, CandidateDescriptor, CandidateCommitments, CompactStatement, EncodeAs, SigningContext, ValidityAttestation}; +use sp_core::H256; +use sp_runtime::{traits::BlakeTwo256, DigestItem, RuntimeAppPublic}; /// Copied from polkadot service just so that this code retains same structure as /// polkadot_service crate. struct Basics { @@ -177,17 +197,81 @@ struct Basics { telemetry: Option, } -impl + ProvideRuntimeApi> EmptyParachainsInherentDataProvider where -C::Api: ParachainHost +impl> EmptyParachainsInherentDataProvider { + pub fn new(client: Arc, parent: Hash) -> Self { + EmptyParachainsInherentDataProvider { client, parent } + } + + pub async fn create( + client: Arc, + parent: Hash, + ) -> Result { + let parent_header = match client.header(parent) { + Ok(Some(h)) => h, + Ok(None) => return Err(InherentError::ParentHeaderNotFound(parent)), + Err(err) => return Err(InherentError::Blockchain(err)), + }; + + Ok(ParachainsInherentData { + bitfields: Vec::new(), + backed_candidates: Vec::new(), + disputes: Vec::new(), + parent_header, + }) + } +} + +/// Creates new development full node with manual seal +pub fn build_full( + sealing: Sealing, + config: Configuration, + mut params: NewFullParams, +) -> Result { + let is_polkadot = config.chain_spec.is_polkadot(); + + params.overseer_message_channel_capacity_override = params + .overseer_message_channel_capacity_override + .map(move |capacity| { + if is_polkadot { + gum::warn!("Channel capacity should _never_ be tampered with on polkadot!"); + } + capacity + }); + + match config.network.network_backend { + sc_network::config::NetworkBackendType::Libp2p => { + new_full::<_, sc_network::NetworkWorker>(sealing, config, params) + } + sc_network::config::NetworkBackendType::Litep2p => { + new_full::<_, sc_network::Litep2pNetworkBackend>(sealing, config, params) + } + } +} + +/// We use MockParachainsInherentDataProvider to insert an empty parachain inherent in the block +/// to satisfy runtime +struct MockParachainsInherentDataProvider + ProvideRuntimeApi> { + pub client: Arc, + pub parent: Hash, + pub keystore: KeystorePtr, +} + +impl + ProvideRuntimeApi> MockParachainsInherentDataProvider +where + C::Api: ParachainHost, { pub fn new(client: Arc, parent: Hash, keystore: KeystorePtr) -> Self { - EmptyParachainsInherentDataProvider { client, parent, keystore } + MockParachainsInherentDataProvider { + client, + parent, + keystore, + } } pub async fn create( client: Arc, parent: Hash, - keystore: KeystorePtr + keystore: KeystorePtr, ) -> Result { let parent_header_relay = match client.header(parent) { Ok(Some(h)) => h, @@ -195,7 +279,10 @@ C::Api: ParachainHost Err(err) => return Err(InherentError::Blockchain(err)), }; - let parent_hash = client.hash(parent_header_relay.number.saturating_sub(1)).unwrap().unwrap(); + let parent_hash = client + .hash(parent_header_relay.number.saturating_sub(1)) + .unwrap() + .unwrap(); let parent_header = match client.header(parent_hash) { Ok(Some(h)) => h, @@ -209,23 +296,24 @@ C::Api: ParachainHost // we generate a signature for the parachain assigned to that core // To retrieve the validator keys, cal runtime api: - // this following piece of code predicts whether the validator is assigned to a particular + // this following piece of code predicts whether the validator is assigned to a particular // core where a candidate for a parachain needs to be created let runtime_api = client.runtime_api(); let para_authorities = runtime_api.validators(parent_hash).unwrap(); let claim_queue = runtime_api.claim_queue(parent_hash).unwrap(); let (groups, rotation_info) = runtime_api.validator_groups(parent_hash).unwrap(); - let rotations_since_session_start = - (parent_header.number - rotation_info.session_start_block) / rotation_info.group_rotation_frequency; - + let rotations_since_session_start = (parent_header.number + - rotation_info.session_start_block) + / rotation_info.group_rotation_frequency; + // Get all the available keys - let available_keys = keystore.keys(polkadot_primitives::PARACHAIN_KEY_TYPE_ID).unwrap(); - - let slot_number = AuraInherentType::from( - u64::from(parent_header.number), - ); - + let available_keys = keystore + .keys(polkadot_primitives::PARACHAIN_KEY_TYPE_ID) + .unwrap(); + + let slot_number = AuraInherentType::from(u64::from(parent_header.number)); + let parachain_mocked_header = sp_runtime::generic::Header:: { parent_hash: Default::default(), number: parent_header.number, @@ -233,68 +321,76 @@ C::Api: ParachainHost extrinsics_root: Default::default(), digest: sp_runtime::generic::Digest { logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot_number.encode())], - } + }, }; let availability_cores = runtime_api.availability_cores(parent_hash).unwrap(); let session_idx = runtime_api.session_index_for_child(parent_hash).unwrap(); let all_validators = runtime_api.validators(parent_hash).unwrap(); let availability_bitvec = availability_bitvec(1, availability_cores.len()); - let signature_ctx = SigningContext { parent_hash: parent, session_index: session_idx, }; - log::info!("availability bitvec is {:?}", availability_bitvec); // we generate the availability bitfield sigs let bitfields: Vec> = all_validators - .iter() - .enumerate() - .map(|(i, public)| { - keystore_sign( - &keystore, - availability_bitvec.clone(), - &signature_ctx, - ValidatorIndex(i as u32), - &public - ).unwrap().unwrap() - }) - .collect(); - - log::info!("bitfields {:?}", bitfields); + .iter() + .enumerate() + .map(|(i, public)| { + keystore_sign( + &keystore, + availability_bitvec.clone(), + &signature_ctx, + ValidatorIndex(i as u32), + &public, + ) + .unwrap() + .unwrap() + }) + .collect(); let collator_pair = CollatorPair::generate().0; let mut backed_cand: Vec> = vec![]; for (core, para) in claim_queue { - let group_assigned_to_core = core.0 + rotations_since_session_start % groups.len() as u32; + let group_assigned_to_core = + core.0 + rotations_since_session_start % groups.len() as u32; let indices_associated_to_core = groups.get(group_assigned_to_core as usize).unwrap(); for index in indices_associated_to_core { let validator_keys_to_find = para_authorities.get(index.0 as usize).unwrap(); // Iterate keys until we find an eligible one, or run out of candidates. for type_public_pair in &available_keys { - if let Ok(validator) = polkadot_primitives::ValidatorId::from_slice(&type_public_pair) { + if let Ok(validator) = + polkadot_primitives::ValidatorId::from_slice(&type_public_pair) + { if validator_keys_to_find == &validator { - let persisted_validation_data = runtime_api.persisted_validation_data(parent_hash, para[0], OccupiedCoreAssumption::Included).unwrap().unwrap(); - log::info!("parent_hash is {:?}", parent_hash); - - log::info!("validation data is {:?}", persisted_validation_data); - log::info!("validation data encoded is {:?}", persisted_validation_data.encode()); - log::info!("parent number is is {:?}", parent_header.number); + let persisted_validation_data = runtime_api + .persisted_validation_data( + parent_hash, + para[0], + OccupiedCoreAssumption::Included, + ) + .unwrap() + .unwrap(); let persisted_validation_data_hash = persisted_validation_data.hash(); - let validation_code_hash = runtime_api.validation_code_hash(parent_hash, para[0], OccupiedCoreAssumption::Included).unwrap().unwrap(); + let validation_code_hash = runtime_api + .validation_code_hash( + parent_hash, + para[0], + OccupiedCoreAssumption::Included, + ) + .unwrap() + .unwrap(); let pov_hash = Default::default(); - let payload = - polkadot_primitives::collator_signature_payload( - &parent_hash, - ¶[0], - &persisted_validation_data_hash, - &pov_hash, - &validation_code_hash, - ); + let payload = polkadot_primitives::collator_signature_payload( + &parent_hash, + ¶[0], + &persisted_validation_data_hash, + &pov_hash, + &validation_code_hash, + ); let collator_signature = collator_pair.sign(&payload); - let prev_head = persisted_validation_data.parent_head; let candidate = CommittedCandidateReceipt:: { descriptor: CandidateDescriptor:: { para_id: para[0], @@ -324,15 +420,16 @@ C::Api: ParachainHost session_index: session_idx, }; - log::info!("before sig"); - let signature = keystore_sign( &keystore, payload, &signature_ctx, *index, - &validator - ).unwrap().unwrap().benchmark_signature(); + &validator, + ) + .unwrap() + .unwrap() + .benchmark_signature(); log::info!("after sig"); @@ -344,21 +441,12 @@ C::Api: ParachainHost bitvec::bitvec![u8, bitvec::order::Lsb0; 1; indices_associated_to_core.len()], Some(core), )); - - - - - // Only in this case, we need to create a candidate - log::info!("found public key"); - log::info!("validity_votes {:?}", validity_votes.clone()); } } } } } - log::info!("backed_cand {:?}", backed_cand); - Ok(ParachainsInherentData { bitfields: bitfields, backed_candidates: backed_cand, @@ -370,20 +458,42 @@ C::Api: ParachainHost #[async_trait::async_trait] impl + ProvideRuntimeApi> sp_inherents::InherentDataProvider - for EmptyParachainsInherentDataProvider - - where C::Api: ParachainHost + for MockParachainsInherentDataProvider +where + C::Api: ParachainHost, + C: AuxStore, { async fn provide_inherent_data( &self, dst_inherent_data: &mut sp_inherents::InherentData, ) -> Result<(), sp_inherents::Error> { - let inherent_data = - EmptyParachainsInherentDataProvider::create(self.client.clone(), self.parent, self.keystore.clone()) - .await - .map_err(|e| sp_inherents::Error::Application(Box::new(e)))?; + let maybe_para_selector = self + .client + .get_aux(PARA_INHERENT_SELECTOR_AUX_KEY) + .expect("Should be able to query aux storage; qed"); + + let inherent_data = { + if let Some(aux) = maybe_para_selector { + if aux == true.encode() { + MockParachainsInherentDataProvider::create( + self.client.clone(), + self.parent, + self.keystore.clone(), + ) + .await + .map_err(|e| sp_inherents::Error::Application(Box::new(e)))? + } else { + EmptyParachainsInherentDataProvider::create(self.client.clone(), self.parent) + .await + .map_err(|e| sp_inherents::Error::Application(Box::new(e)))? + } + } else { + EmptyParachainsInherentDataProvider::create(self.client.clone(), self.parent) + .await + .map_err(|e| sp_inherents::Error::Application(Box::new(e)))? + } + }; - log::info!("inherent data {:?}", inherent_data); dst_inherent_data.put_data( polkadot_primitives::PARACHAINS_INHERENT_IDENTIFIER, &inherent_data, @@ -400,33 +510,6 @@ impl + ProvideRuntimeApi> sp_inherents::InherentD } } -/// Creates new development full node with manual seal -pub fn build_full( - sealing: Sealing, - config: Configuration, - mut params: NewFullParams, -) -> Result { - let is_polkadot = config.chain_spec.is_polkadot(); - - params.overseer_message_channel_capacity_override = params - .overseer_message_channel_capacity_override - .map(move |capacity| { - if is_polkadot { - gum::warn!("Channel capacity should _never_ be tampered with on polkadot!"); - } - capacity - }); - - match config.network.network_backend { - sc_network::config::NetworkBackendType::Libp2p => { - new_full::<_, sc_network::NetworkWorker>(sealing, config, params) - } - sc_network::config::NetworkBackendType::Litep2p => { - new_full::<_, sc_network::Litep2pNetworkBackend>(sealing, config, params) - } - } -} - /// We store past timestamp we created in the aux storage, which enable us to return timestamp which is increased by /// slot duration from previous timestamp or current timestamp if in reality more time is passed. fn get_next_timestamp( @@ -503,6 +586,10 @@ fn new_full< let net_config = sc_network::config::FullNetworkConfiguration::<_, _, Network>::new(&config.network); + // Create channels for mocked parachain candidates. + let (downward_mock_para_inherent_sender, downward_mock_para_inherent_receiver) = + flume::bounded::>(100); + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = service::build_network(service::BuildNetworkParams { config: &config, @@ -615,13 +702,29 @@ fn new_full< create_inherent_data_providers: move |parent, ()| { let client_clone = client_clone.clone(); let keystore = keystore_clone.clone(); + let downward_mock_para_inherent_receiver = downward_mock_para_inherent_receiver.clone(); async move { - let parachain = - EmptyParachainsInherentDataProvider::new( - client_clone.clone(), - parent, - keystore - ); + + let downward_mock_para_inherent_receiver = downward_mock_para_inherent_receiver.clone(); + // here we only take the last one + let para_inherent_decider_messages: Vec> = downward_mock_para_inherent_receiver.drain().collect(); + + // If there is a value to be updated, we update it + if let Some(value) = para_inherent_decider_messages.last() { + client_clone + .insert_aux( + &[(PARA_INHERENT_SELECTOR_AUX_KEY, value.as_slice())], + &[], + ) + .expect("Should be able to write to aux storage; qed"); + + } + + let parachain = MockParachainsInherentDataProvider::new( + client_clone.clone(), + parent, + keystore + ); let timestamp = get_next_timestamp(client_clone, slot_duration); @@ -639,6 +742,13 @@ fn new_full< ); } + // We dont need the flume receiver if we are not a validator + let dev_rpc_data = if role.clone().is_authority() { + Some(downward_mock_para_inherent_sender) + } else { + None + }; + let rpc_extensions_builder = { let client = client.clone(); let transaction_pool = transaction_pool.clone(); @@ -653,6 +763,7 @@ fn new_full< chain_spec: chain_spec.cloned_box(), deny_unsafe, command_sink: command_sink.clone(), + dev_rpc_data: dev_rpc_data.clone(), }; create_dev_rpc_extension(deps).map_err(Into::into) @@ -816,7 +927,7 @@ fn new_partial_basics( }) } -use polkadot_primitives::{ValidatorIndex, ValidatorId, UncheckedSigned, AvailabilityBitfield}; +use polkadot_primitives::{AvailabilityBitfield, UncheckedSigned, ValidatorId, ValidatorIndex}; use sp_keystore::Error as KeystoreError; fn keystore_sign( keystore: &KeystorePtr, @@ -824,18 +935,18 @@ fn keystore_sign( context: &SigningContext, validator_index: ValidatorIndex, key: &ValidatorId, -) -> Result>, KeystoreError> { +) -> Result>, KeystoreError> { let data = payload_data(&payload, context); - let signature = - keystore.sr25519_sign(ValidatorId::ID, key.as_ref(), &data)?.map(|sig| UncheckedSigned::new( - payload, - validator_index, - sig.into(), - )); + let signature = keystore + .sr25519_sign(ValidatorId::ID, key.as_ref(), &data)? + .map(|sig| UncheckedSigned::new(payload, validator_index, sig.into())); Ok(signature) } -fn payload_data(payload: &Payload, context: &SigningContext) -> Vec { +fn payload_data( + payload: &Payload, + context: &SigningContext, +) -> Vec { // equivalent to (`real_payload`, context).encode() let mut out = payload.encode_as(); out.extend(context.encode()); @@ -843,16 +954,16 @@ fn payload_data(payload: &Payload, context: &Signing } /// Create an `AvailabilityBitfield` where `concluding` is a map where each key is a core index - /// that is concluding and `cores` is the total number of cores in the system. - fn availability_bitvec(used_cores: usize, cores: usize) -> AvailabilityBitfield { - let mut bitfields = bitvec::bitvec![u8, bitvec::order::Lsb0; 0; 0]; - for i in 0..cores { - if i < used_cores { - bitfields.push(true); - } else { - bitfields.push(false) - } +/// that is concluding and `cores` is the total number of cores in the system. +fn availability_bitvec(used_cores: usize, cores: usize) -> AvailabilityBitfield { + let mut bitfields = bitvec::bitvec![u8, bitvec::order::Lsb0; 0; 0]; + for i in 0..cores { + if i < used_cores { + bitfields.push(true); + } else { + bitfields.push(false) } + } - bitfields.into() - } \ No newline at end of file + bitfields.into() +} diff --git a/solo-chains/node/tanssi-relay-service/src/lib.rs b/solo-chains/node/tanssi-relay-service/src/lib.rs index 0ad8f1d95..46a0e5030 100644 --- a/solo-chains/node/tanssi-relay-service/src/lib.rs +++ b/solo-chains/node/tanssi-relay-service/src/lib.rs @@ -17,3 +17,5 @@ pub mod chain_spec; pub mod dev_service; + +pub mod dev_rpcs; From d46adf865416f40cc4850a2278ec1267ced2e46b Mon Sep 17 00:00:00 2001 From: girazoki Date: Thu, 14 Nov 2024 19:04:24 +0100 Subject: [PATCH 04/14] wip --- .../node/tanssi-relay-service/src/dev_rpcs.rs | 2 + .../tanssi-relay-service/src/dev_service.rs | 12 +++-- .../test_paras_candidate_inherent.ts | 45 +++++++++++++++++++ test/util/relayInterface.ts | 13 ++++-- 4 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts diff --git a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs index 5ccd24e5a..32eb11dbe 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs @@ -47,6 +47,7 @@ pub struct DevRpc { #[jsonrpsee::core::async_trait] impl DevApiServer for DevRpc { async fn enable_para_inherent_candidate(&self) -> RpcResult<()> { + log::info!("entering here"); let mock_para_inherent_channel = self.mock_para_inherent_channel.clone(); // Push the message to the shared channel where it will be queued up // to be injected in to an upcoming block. @@ -55,6 +56,7 @@ impl DevApiServer for DevRpc { .await .map_err(|err| internal_err(err.to_string()))?; + log::info!("SENEDING ENABLE"); Ok(()) } diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index c2dc8ac26..a76fa5eb5 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -279,10 +279,7 @@ where Err(err) => return Err(InherentError::Blockchain(err)), }; - let parent_hash = client - .hash(parent_header_relay.number.saturating_sub(1)) - .unwrap() - .unwrap(); + let parent_hash = parent; let parent_header = match client.header(parent_hash) { Ok(Some(h)) => h, @@ -364,7 +361,7 @@ where polkadot_primitives::ValidatorId::from_slice(&type_public_pair) { if validator_keys_to_find == &validator { - let persisted_validation_data = runtime_api + let mut persisted_validation_data = runtime_api .persisted_validation_data( parent_hash, para[0], @@ -373,6 +370,9 @@ where .unwrap() .unwrap(); + // if we dont do this we have a backed candidate every 2 blocks + persisted_validation_data.relay_parent_storage_root = parent_header.state_root; + let persisted_validation_data_hash = persisted_validation_data.hash(); let validation_code_hash = runtime_api .validation_code_hash( @@ -431,8 +431,6 @@ where .unwrap() .benchmark_signature(); - log::info!("after sig"); - let validity_votes = vec![ValidityAttestation::Explicit(signature)]; backed_cand.push(BackedCandidate::::new( diff --git a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts new file mode 100644 index 000000000..ccbb1fb92 --- /dev/null +++ b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts @@ -0,0 +1,45 @@ +import "@tanssi/api-augment"; +import { describeSuite, customDevRpcRequest, expect } from "@moonwall/cli"; +import { ApiPromise } from "@polkadot/api"; +import { jumpBlocks, jumpSessions, jumpToSession } from "util/block"; +import { filterAndApply } from "@moonwall/util"; +import { EventRecord } from "@polkadot/types/interfaces"; +import { bool, u32, u8, Vec } from "@polkadot/types-codec"; +import { before } from "node:test"; +import { getHeaderFromRelay } from "util/relayInterface.ts"; + +describeSuite({ + id: "DTR1401", + title: "Paras inherent tests", + foundationMethods: "dev", + + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + + before(async () => { + polkadotJs = context.polkadotJs(); + }); + + it({ + id: "E01", + title: "Paras heads should be updated every block", + test: async function () { + const parasHeadGenesis = await context.polkadotJs().query.paras.heads(2000); + await context.createBlock(); + // Send RPC call to enable para inherent candidate generation + await customDevRpcRequest("mock_enableParaInherentCandidate", []); + // Since collators are not assigned until session 2, we need to go till session 2 to actually see heads being injected + await jumpToSession(context, 3); + await context.createBlock(); + const parasHeadAfterOneBlock = await context.polkadotJs().query.paras.heads(2000); + expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadGenesis); + await context.createBlock(); + // we create one more block to test we are persisting candidates every block + const parasHeadAfterTwoBlocks = await context.polkadotJs().query.paras.heads(2000); + expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadAfterTwoBlocks); + const header2000 = await getHeaderFromRelay(context.polkadotJs(), 2000); + expect(header2000.number.toBigInt()).to.be.equal(31n); + }, + }); + }, +}); diff --git a/test/util/relayInterface.ts b/test/util/relayInterface.ts index 80c92cb5f..632ff206f 100644 --- a/test/util/relayInterface.ts +++ b/test/util/relayInterface.ts @@ -1,9 +1,16 @@ import { ApiPromise } from "@polkadot/api"; -import type { Header, ParaId } from "@polkadot/types/interfaces"; +import type { Header, ParaId, HeadData } from "@polkadot/types/interfaces"; +import { u8aToHex, u8aToU8a, isU8a, hexToU8a, compactToU8a, compactFromU8a, compactFromU8aLim } from "@polkadot/util" +import { bool, u32, u8, Bytes } from "@polkadot/types-codec"; +import { TypeRegistry } from "@polkadot/types"; export async function getHeaderFromRelay(relayApi: ApiPromise, paraId: ParaId): Promise
{ // Get the latest header from relay storage const encoded = await relayApi.query.paras.heads(paraId); - const header = await relayApi.createType("Header", encoded); + const registry = new TypeRegistry(); + const headerEncoded: HeadData = await relayApi.createType("HeadData", encoded.toHex()); + const nonEncodedHeader = new Bytes(registry, headerEncoded.toU8a(true)).toHex() + + const header = await relayApi.createType("SpRuntimeHeader", nonEncodedHeader); return header; -} +} \ No newline at end of file From 72d8875f0c283deed9b2b36d80a3085ef51942d6 Mon Sep 17 00:00:00 2001 From: girazoki Date: Thu, 14 Nov 2024 19:07:37 +0100 Subject: [PATCH 05/14] wip --- .../paras-candidate-inherent/test_paras_candidate_inherent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts index ccbb1fb92..ba5a2984e 100644 --- a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts +++ b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts @@ -36,7 +36,7 @@ describeSuite({ await context.createBlock(); // we create one more block to test we are persisting candidates every block const parasHeadAfterTwoBlocks = await context.polkadotJs().query.paras.heads(2000); - expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadAfterTwoBlocks); + expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadAfterTwoBlocks); const header2000 = await getHeaderFromRelay(context.polkadotJs(), 2000); expect(header2000.number.toBigInt()).to.be.equal(31n); }, From 3c32ffc31bbadada4f082be98b2de0609a137abc Mon Sep 17 00:00:00 2001 From: girazoki Date: Mon, 18 Nov 2024 12:47:37 +0100 Subject: [PATCH 06/14] test related to validator rewards --- .../test_external_validator_rewards.ts | 68 +++++++++++++++++++ .../test_paras_candidate_inherent.ts | 2 +- ...lashes_are_removed_after_bonding_period.ts | 4 +- test/util/relayInterface.ts | 6 +- 4 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts diff --git a/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts b/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts new file mode 100644 index 000000000..b21b5d300 --- /dev/null +++ b/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts @@ -0,0 +1,68 @@ +import "@tanssi/api-augment"; +import { describeSuite, customDevRpcRequest, expect, beforeAll } from "@moonwall/cli"; +import { ApiPromise, Keyring } from "@polkadot/api"; +import { jumpToSession } from "util/block"; +import { before } from "node:test"; + +describeSuite({ + id: "DTR1601", + title: "Paras inherent tests", + foundationMethods: "dev", + + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + }); + + it({ + id: "E01", + title: "para candidates should trigger reward info", + test: async function () { + const keyring = new Keyring({ type: "sr25519" }); + const aliceStash = keyring.addFromUri("//Alice//stash"); + await context.createBlock(); + // Send RPC call to enable para inherent candidate generation + await customDevRpcRequest("mock_enableParaInherentCandidate", []); + // Since collators are not assigned until session 2, we need to go till session 2 to actually see heads being injected + await jumpToSession(context, 3); + await context.createBlock(); + + // we are still in era 0 + const validatorRewards = await context + .polkadotJs() + .query.externalValidatorsRewards.rewardPointsForEra(0); + const totalRewards = validatorRewards.total.toBigInt(); + + expect(totalRewards).to.be.greaterThan(0n); + // All of them come from alice as she is the only one validating candidates + expect(validatorRewards.individual.toHuman()[aliceStash.address]).to.be.eq(totalRewards.toString()); + }, + }); + + it({ + id: "E02", + title: "Check rewards storage clears after historyDepth", + test: async function () { + const sessionsPerEra = await polkadotJs.consts.externalValidators.sessionsPerEra; + const historyDepth = await polkadotJs.consts.externalValidatorsRewards.historyDepth; + + const currentIndex = await polkadotJs.query.session.currentIndex(); + + const targetSession = + currentIndex.toNumber() + sessionsPerEra.toNumber() * (historyDepth.toNumber() + 1); + + await jumpToSession(context, targetSession); + + const validatorRewards = await context + .polkadotJs() + .query.externalValidatorsRewards.rewardPointsForEra(0); + const totalRewards = validatorRewards.total.toBigInt(); + + // rewards should have expired + expect(totalRewards).to.be.equal(0n); + }, + }); + }, +}); diff --git a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts index ba5a2984e..ccbb1fb92 100644 --- a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts +++ b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts @@ -36,7 +36,7 @@ describeSuite({ await context.createBlock(); // we create one more block to test we are persisting candidates every block const parasHeadAfterTwoBlocks = await context.polkadotJs().query.paras.heads(2000); - expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadAfterTwoBlocks); + expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadAfterTwoBlocks); const header2000 = await getHeaderFromRelay(context.polkadotJs(), 2000); expect(header2000.number.toBigInt()).to.be.equal(31n); }, diff --git a/test/suites/dev-tanssi-relay/slashes/test_slashes_are_removed_after_bonding_period.ts b/test/suites/dev-tanssi-relay/slashes/test_slashes_are_removed_after_bonding_period.ts index 91eb73304..3d289f1e0 100644 --- a/test/suites/dev-tanssi-relay/slashes/test_slashes_are_removed_after_bonding_period.ts +++ b/test/suites/dev-tanssi-relay/slashes/test_slashes_are_removed_after_bonding_period.ts @@ -78,8 +78,8 @@ describeSuite({ .signAsync(alice); await context.createBlock([addAliceFromInvulnerables]); - const sessionsPerEra = await polkadotJs.consts.externalValidators.sessionsPerEra; - const bondingPeriod = await polkadotJs.consts.externalValidatorSlashes.bondingDuration; + const sessionsPerEra = (await polkadotJs.consts.externalValidators.sessionsPerEra).toNumber(); + const bondingPeriod = (await polkadotJs.consts.externalValidatorSlashes.bondingDuration).toNumber(); const currentIndex = await polkadotJs.query.session.currentIndex(); diff --git a/test/util/relayInterface.ts b/test/util/relayInterface.ts index 632ff206f..95b219833 100644 --- a/test/util/relayInterface.ts +++ b/test/util/relayInterface.ts @@ -1,6 +1,6 @@ import { ApiPromise } from "@polkadot/api"; import type { Header, ParaId, HeadData } from "@polkadot/types/interfaces"; -import { u8aToHex, u8aToU8a, isU8a, hexToU8a, compactToU8a, compactFromU8a, compactFromU8aLim } from "@polkadot/util" +import { u8aToHex, u8aToU8a, isU8a, hexToU8a, compactToU8a, compactFromU8a, compactFromU8aLim } from "@polkadot/util"; import { bool, u32, u8, Bytes } from "@polkadot/types-codec"; import { TypeRegistry } from "@polkadot/types"; @@ -9,8 +9,8 @@ export async function getHeaderFromRelay(relayApi: ApiPromise, paraId: ParaId): const encoded = await relayApi.query.paras.heads(paraId); const registry = new TypeRegistry(); const headerEncoded: HeadData = await relayApi.createType("HeadData", encoded.toHex()); - const nonEncodedHeader = new Bytes(registry, headerEncoded.toU8a(true)).toHex() + const nonEncodedHeader = new Bytes(registry, headerEncoded.toU8a(true)).toHex(); const header = await relayApi.createType("SpRuntimeHeader", nonEncodedHeader); return header; -} \ No newline at end of file +} From 673aeae671219a8ec4f46459d6b1208a298ff3e7 Mon Sep 17 00:00:00 2001 From: girazoki Date: Mon, 18 Nov 2024 12:51:01 +0100 Subject: [PATCH 07/14] =?UTF-8?q?toml=C2=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solo-chains/node/tanssi-relay-service/Cargo.toml | 2 +- solo-chains/node/tanssi-relay-service/src/dev_service.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/Cargo.toml b/solo-chains/node/tanssi-relay-service/Cargo.toml index 76da71989..81706ddfd 100644 --- a/solo-chains/node/tanssi-relay-service/Cargo.toml +++ b/solo-chains/node/tanssi-relay-service/Cargo.toml @@ -47,8 +47,8 @@ sp-api = { workspace = true } sp-authority-discovery = { workspace = true } sp-block-builder = { workspace = true } sp-blockchain = { workspace = true } -sp-consensus-babe = { workspace = true } sp-consensus-aura = { workspace = true } +sp-consensus-babe = { workspace = true } sp-core = { workspace = true, features = [ "std" ] } sp-inherents = { workspace = true, features = [ "std" ] } sp-io = { workspace = true, features = [ "std" ] } diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index a76fa5eb5..d17a6ed4a 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -371,7 +371,8 @@ where .unwrap(); // if we dont do this we have a backed candidate every 2 blocks - persisted_validation_data.relay_parent_storage_root = parent_header.state_root; + persisted_validation_data.relay_parent_storage_root = + parent_header.state_root; let persisted_validation_data_hash = persisted_validation_data.hash(); let validation_code_hash = runtime_api From 0de39fc5a53bc04cf60f246202bc28234b34ad91 Mon Sep 17 00:00:00 2001 From: girazoki Date: Mon, 18 Nov 2024 13:34:25 +0100 Subject: [PATCH 08/14] clippy --- .../node/tanssi-relay-service/src/dev_rpcs.rs | 2 +- .../node/tanssi-relay-service/src/dev_service.rs | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs index 32eb11dbe..49f193a69 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs @@ -17,7 +17,7 @@ //! Development Polkadot service. Adapted from `polkadot_service` crate //! and removed un-necessary components which are not required in dev node. -use codec::{Decode, Encode}; +use codec::Encode; use jsonrpsee::{ core::RpcResult, proc_macros::rpc, diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index d17a6ed4a..e250bd1fc 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -56,7 +56,7 @@ use { run_manual_seal, EngineCommand, ManualSealParams, }, sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}, - sc_keystore::{Keystore, LocalKeystore}, + sc_keystore::Keystore, sc_transaction_pool_api::{OffchainTransactionPoolFactory, TransactionPool}, service::{Configuration, KeystoreContainer, RpcHandlers, TaskManager}, sp_api::ProvideRuntimeApi, @@ -171,10 +171,7 @@ where /// We use EmptyParachainsInherentDataProvider to insert an empty parachain inherent in the block /// to satisfy runtime -struct EmptyParachainsInherentDataProvider> { - pub client: Arc, - pub parent: Hash, -} +struct EmptyParachainsInherentDataProvider; use polkadot_primitives::BackedCandidate; use polkadot_primitives::CollatorPair; @@ -197,12 +194,8 @@ struct Basics { telemetry: Option, } -impl> EmptyParachainsInherentDataProvider { - pub fn new(client: Arc, parent: Hash) -> Self { - EmptyParachainsInherentDataProvider { client, parent } - } - - pub async fn create( +impl EmptyParachainsInherentDataProvider { + pub async fn create>( client: Arc, parent: Hash, ) -> Result { From debc74659f740f6ab12ad05533cb0b09956b9a9e Mon Sep 17 00:00:00 2001 From: girazoki Date: Mon, 18 Nov 2024 13:37:22 +0100 Subject: [PATCH 09/14] linting --- .../test_external_validator_rewards.ts | 1 - .../test_paras_candidate_inherent.ts | 5 +---- test/util/relayInterface.ts | 3 +-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts b/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts index b21b5d300..dc4ef52ea 100644 --- a/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts +++ b/test/suites/dev-tanssi-relay/external-validators-rewards/test_external_validator_rewards.ts @@ -2,7 +2,6 @@ import "@tanssi/api-augment"; import { describeSuite, customDevRpcRequest, expect, beforeAll } from "@moonwall/cli"; import { ApiPromise, Keyring } from "@polkadot/api"; import { jumpToSession } from "util/block"; -import { before } from "node:test"; describeSuite({ id: "DTR1601", diff --git a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts index ccbb1fb92..95c1b125d 100644 --- a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts +++ b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts @@ -1,10 +1,7 @@ import "@tanssi/api-augment"; import { describeSuite, customDevRpcRequest, expect } from "@moonwall/cli"; import { ApiPromise } from "@polkadot/api"; -import { jumpBlocks, jumpSessions, jumpToSession } from "util/block"; -import { filterAndApply } from "@moonwall/util"; -import { EventRecord } from "@polkadot/types/interfaces"; -import { bool, u32, u8, Vec } from "@polkadot/types-codec"; +import { jumpToSession } from "util/block"; import { before } from "node:test"; import { getHeaderFromRelay } from "util/relayInterface.ts"; diff --git a/test/util/relayInterface.ts b/test/util/relayInterface.ts index 95b219833..048b3ad1a 100644 --- a/test/util/relayInterface.ts +++ b/test/util/relayInterface.ts @@ -1,7 +1,6 @@ import { ApiPromise } from "@polkadot/api"; import type { Header, ParaId, HeadData } from "@polkadot/types/interfaces"; -import { u8aToHex, u8aToU8a, isU8a, hexToU8a, compactToU8a, compactFromU8a, compactFromU8aLim } from "@polkadot/util"; -import { bool, u32, u8, Bytes } from "@polkadot/types-codec"; +import { Bytes } from "@polkadot/types-codec"; import { TypeRegistry } from "@polkadot/types"; export async function getHeaderFromRelay(relayApi: ApiPromise, paraId: ParaId): Promise
{ From aefd1c6ef057a806294e13a16c0ee5a1b2c22f9d Mon Sep 17 00:00:00 2001 From: girazoki Date: Mon, 18 Nov 2024 13:39:44 +0100 Subject: [PATCH 10/14] more lint --- .../test_paras_candidate_inherent.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts index 95c1b125d..b1329ec6c 100644 --- a/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts +++ b/test/suites/dev-tanssi-relay/paras-candidate-inherent/test_paras_candidate_inherent.ts @@ -1,8 +1,7 @@ import "@tanssi/api-augment"; -import { describeSuite, customDevRpcRequest, expect } from "@moonwall/cli"; +import { describeSuite, customDevRpcRequest, expect, beforeAll } from "@moonwall/cli"; import { ApiPromise } from "@polkadot/api"; import { jumpToSession } from "util/block"; -import { before } from "node:test"; import { getHeaderFromRelay } from "util/relayInterface.ts"; describeSuite({ @@ -13,7 +12,7 @@ describeSuite({ testCases: ({ it, context }) => { let polkadotJs: ApiPromise; - before(async () => { + beforeAll(async () => { polkadotJs = context.polkadotJs(); }); @@ -21,18 +20,18 @@ describeSuite({ id: "E01", title: "Paras heads should be updated every block", test: async function () { - const parasHeadGenesis = await context.polkadotJs().query.paras.heads(2000); + const parasHeadGenesis = await polkadotJs.query.paras.heads(2000); await context.createBlock(); // Send RPC call to enable para inherent candidate generation await customDevRpcRequest("mock_enableParaInherentCandidate", []); // Since collators are not assigned until session 2, we need to go till session 2 to actually see heads being injected await jumpToSession(context, 3); await context.createBlock(); - const parasHeadAfterOneBlock = await context.polkadotJs().query.paras.heads(2000); + const parasHeadAfterOneBlock = await polkadotJs.query.paras.heads(2000); expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadGenesis); await context.createBlock(); // we create one more block to test we are persisting candidates every block - const parasHeadAfterTwoBlocks = await context.polkadotJs().query.paras.heads(2000); + const parasHeadAfterTwoBlocks = await polkadotJs.query.paras.heads(2000); expect(parasHeadAfterOneBlock).to.not.be.eq(parasHeadAfterTwoBlocks); const header2000 = await getHeaderFromRelay(context.polkadotJs(), 2000); expect(header2000.number.toBigInt()).to.be.equal(31n); From 7a77f4544590ef43fc6fdb265730ab8b62cc398a Mon Sep 17 00:00:00 2001 From: girazoki Date: Tue, 19 Nov 2024 11:00:13 +0100 Subject: [PATCH 11/14] start cleaning --- .../tanssi-relay-service/src/dev_service.rs | 67 ++++++++----------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index e250bd1fc..1ece7373d 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -30,9 +30,8 @@ //! 10. If amount of time passed between two block is less than slot duration, we emulate passing of time babe block import and runtime //! by incrementing timestamp by slot duration. -use crate::dev_rpcs::DevRpc; - use { + crate::dev_rpcs::{DevApiServer, DevRpc}, async_io::Timer, babe::{BabeBlockImport, BabeLink}, codec::{Decode, Encode}, @@ -44,7 +43,12 @@ use { polkadot_core_primitives::{AccountId, Balance, Block, Hash, Nonce}, polkadot_node_core_parachains_inherent::Error as InherentError, polkadot_overseer::Handle, - polkadot_primitives::{runtime_api::ParachainHost, InherentData as ParachainsInherentData}, + polkadot_primitives::{ + runtime_api::ParachainHost, BackedCandidate, CandidateCommitments, CandidateDescriptor, + CollatorPair, CommittedCandidateReceipt, CompactStatement, EncodeAs, + InherentData as ParachainsInherentData, OccupiedCoreAssumption, SigningContext, + ValidityAttestation, + }, polkadot_rpc::{DenyUnsafe, RpcExtension}, polkadot_service::{ BlockT, Error, IdentifyVariant, NewFullParams, OverseerGen, SelectRelayChain, @@ -62,15 +66,16 @@ use { sp_api::ProvideRuntimeApi, sp_block_builder::BlockBuilder, sp_blockchain::{HeaderBackend, HeaderMetadata}, + sp_consensus_aura::{inherents::InherentType as AuraInherentType, AURA_ENGINE_ID}, sp_consensus_babe::SlotDuration, - sp_core::ByteArray, + sp_core::{ByteArray, Pair, H256}, sp_keystore::KeystorePtr, + sp_runtime::{traits::BlakeTwo256, DigestItem, RuntimeAppPublic}, std::{cmp::max, ops::Add, sync::Arc, time::Duration}, telemetry::{Telemetry, TelemetryWorker, TelemetryWorkerHandle}, }; -use crate::dev_rpcs::DevApiServer; - +// We use this key to store whether we want the para inherent mocker to be active const PARA_INHERENT_SELECTOR_AUX_KEY: &[u8] = b"__DEV_PARA_INHERENT_SELECTOR"; pub type FullBackend = service::TFullBackend; @@ -173,17 +178,6 @@ where /// to satisfy runtime struct EmptyParachainsInherentDataProvider; -use polkadot_primitives::BackedCandidate; -use polkadot_primitives::CollatorPair; -use polkadot_primitives::OccupiedCoreAssumption; -use polkadot_primitives::{ - CandidateCommitments, CandidateDescriptor, CommittedCandidateReceipt, CompactStatement, - EncodeAs, SigningContext, ValidityAttestation, -}; -use sp_consensus_aura::{inherents::InherentType as AuraInherentType, AURA_ENGINE_ID}; -use sp_core::Pair; -use sp_core::H256; -use sp_runtime::{traits::BlakeTwo256, DigestItem, RuntimeAppPublic}; /// Copied from polkadot service just so that this code retains same structure as /// polkadot_service crate. struct Basics { @@ -241,8 +235,10 @@ pub fn build_full( } } -/// We use MockParachainsInherentDataProvider to insert an empty parachain inherent in the block -/// to satisfy runtime +/// We use MockParachainsInherentDataProvider to insert an parachain inherent with mocked +/// candidates +/// We detect whether any of the keys in our keystore is assigned to a core and provide +/// a mocked candidate in such core struct MockParachainsInherentDataProvider + ProvideRuntimeApi> { pub client: Arc, pub parent: Hash, @@ -266,19 +262,12 @@ where parent: Hash, keystore: KeystorePtr, ) -> Result { - let parent_header_relay = match client.header(parent) { + let parent_header = match client.header(parent) { Ok(Some(h)) => h, Ok(None) => return Err(InherentError::ParentHeaderNotFound(parent)), Err(err) => return Err(InherentError::Blockchain(err)), }; - let parent_hash = parent; - - let parent_header = match client.header(parent_hash) { - Ok(Some(h)) => h, - Ok(None) => return Err(InherentError::ParentHeaderNotFound(parent)), - Err(err) => return Err(InherentError::Blockchain(err)), - }; // Strategy: // we usually have 1 validator per core, and we usually run with --alice // the idea is that at least alice will be assigned to one core @@ -290,9 +279,9 @@ where // core where a candidate for a parachain needs to be created let runtime_api = client.runtime_api(); - let para_authorities = runtime_api.validators(parent_hash).unwrap(); - let claim_queue = runtime_api.claim_queue(parent_hash).unwrap(); - let (groups, rotation_info) = runtime_api.validator_groups(parent_hash).unwrap(); + let para_authorities = runtime_api.validators(parent).unwrap(); + let claim_queue = runtime_api.claim_queue(parent).unwrap(); + let (groups, rotation_info) = runtime_api.validator_groups(parent).unwrap(); let rotations_since_session_start = (parent_header.number - rotation_info.session_start_block) / rotation_info.group_rotation_frequency; @@ -313,9 +302,9 @@ where logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot_number.encode())], }, }; - let availability_cores = runtime_api.availability_cores(parent_hash).unwrap(); - let session_idx = runtime_api.session_index_for_child(parent_hash).unwrap(); - let all_validators = runtime_api.validators(parent_hash).unwrap(); + let availability_cores = runtime_api.availability_cores(parent).unwrap(); + let session_idx = runtime_api.session_index_for_child(parent).unwrap(); + let all_validators = runtime_api.validators(parent).unwrap(); let availability_bitvec = availability_bitvec(1, availability_cores.len()); let signature_ctx = SigningContext { @@ -356,7 +345,7 @@ where if validator_keys_to_find == &validator { let mut persisted_validation_data = runtime_api .persisted_validation_data( - parent_hash, + parent, para[0], OccupiedCoreAssumption::Included, ) @@ -370,7 +359,7 @@ where let persisted_validation_data_hash = persisted_validation_data.hash(); let validation_code_hash = runtime_api .validation_code_hash( - parent_hash, + parent, para[0], OccupiedCoreAssumption::Included, ) @@ -378,7 +367,7 @@ where .unwrap(); let pov_hash = Default::default(); let payload = polkadot_primitives::collator_signature_payload( - &parent_hash, + &parent, ¶[0], &persisted_validation_data_hash, &pov_hash, @@ -388,7 +377,7 @@ where let candidate = CommittedCandidateReceipt:: { descriptor: CandidateDescriptor:: { para_id: para[0], - relay_parent: parent_hash, + relay_parent: parent, collator: collator_pair.public(), persisted_validation_data_hash, pov_hash, @@ -410,7 +399,7 @@ where let payload = CompactStatement::Valid(candidate_hash); let signature_ctx = SigningContext { - parent_hash: parent_hash, + parent_hash: parent, session_index: session_idx, }; @@ -443,7 +432,7 @@ where bitfields: bitfields, backed_candidates: backed_cand, disputes: Vec::new(), - parent_header: parent_header_relay, + parent_header, }) } } From 5d4929d827b151997a2f66008dad95b6e97eaae4 Mon Sep 17 00:00:00 2001 From: girazoki Date: Tue, 19 Nov 2024 11:09:36 +0100 Subject: [PATCH 12/14] work --- .../tanssi-relay-service/src/dev_service.rs | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index 1ece7373d..7b4a93372 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -279,20 +279,29 @@ where // core where a candidate for a parachain needs to be created let runtime_api = client.runtime_api(); - let para_authorities = runtime_api.validators(parent).unwrap(); + // we get all validators + + // we get the current claim queue to know core availability let claim_queue = runtime_api.claim_queue(parent).unwrap(); + + // we get the validator groups let (groups, rotation_info) = runtime_api.validator_groups(parent).unwrap(); + + // we calculate rotation since start, which will define the core assignation + // to validators let rotations_since_session_start = (parent_header.number - rotation_info.session_start_block) / rotation_info.group_rotation_frequency; - // Get all the available keys + // Get all the available keys in the keystore let available_keys = keystore .keys(polkadot_primitives::PARACHAIN_KEY_TYPE_ID) .unwrap(); + // create a slot number identical to the parent block num let slot_number = AuraInherentType::from(u64::from(parent_header.number)); + // create a mocked header let parachain_mocked_header = sp_runtime::generic::Header:: { parent_hash: Default::default(), number: parent_header.number, @@ -302,9 +311,17 @@ where logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot_number.encode())], }, }; + + // retrieve availability cores let availability_cores = runtime_api.availability_cores(parent).unwrap(); + + // retrieve current session_idx let session_idx = runtime_api.session_index_for_child(parent).unwrap(); + + // retrieve all validators let all_validators = runtime_api.validators(parent).unwrap(); + + // construct full availability bitvec let availability_bitvec = availability_bitvec(1, availability_cores.len()); let signature_ctx = SigningContext { @@ -313,6 +330,9 @@ where }; // we generate the availability bitfield sigs + // TODO: here we assume all validator keys are able to sign with our keystore + // we need to make sure the key is there before we try to sign + // this is mostly to indicate that the erasure coding chunks where received by all val let bitfields: Vec> = all_validators .iter() .enumerate() @@ -329,20 +349,28 @@ where }) .collect(); + // generate a random collator pair let collator_pair = CollatorPair::generate().0; let mut backed_cand: Vec> = vec![]; + + // iterate over every core|para pair for (core, para) in claim_queue { + // check which group is assigned to each core let group_assigned_to_core = core.0 + rotations_since_session_start % groups.len() as u32; + // check validator indices associated to the core let indices_associated_to_core = groups.get(group_assigned_to_core as usize).unwrap(); for index in indices_associated_to_core { - let validator_keys_to_find = para_authorities.get(index.0 as usize).unwrap(); + // fetch validator keys + let validator_keys_to_find = all_validators.get(index.0 as usize).unwrap(); // Iterate keys until we find an eligible one, or run out of candidates. for type_public_pair in &available_keys { if let Ok(validator) = polkadot_primitives::ValidatorId::from_slice(&type_public_pair) { + // if we find the validator in keystore, we try to create a backed cand if validator_keys_to_find == &validator { + // we work with the previous included data let mut persisted_validation_data = runtime_api .persisted_validation_data( parent, @@ -353,10 +381,12 @@ where .unwrap(); // if we dont do this we have a backed candidate every 2 blocks + // TODO: figure out why persisted_validation_data.relay_parent_storage_root = parent_header.state_root; let persisted_validation_data_hash = persisted_validation_data.hash(); + // retrieve the validation code hash let validation_code_hash = runtime_api .validation_code_hash( parent, @@ -366,6 +396,7 @@ where .unwrap() .unwrap(); let pov_hash = Default::default(); + // generate a fake collator signature let payload = polkadot_primitives::collator_signature_payload( &parent, ¶[0], @@ -374,6 +405,7 @@ where &validation_code_hash, ); let collator_signature = collator_pair.sign(&payload); + // generate a candidate with most of the values mocked let candidate = CommittedCandidateReceipt:: { descriptor: CandidateDescriptor:: { para_id: para[0], @@ -403,6 +435,7 @@ where session_index: session_idx, }; + // sign the candidate with the validator key let signature = keystore_sign( &keystore, payload, @@ -414,8 +447,10 @@ where .unwrap() .benchmark_signature(); + // construct a validity vote let validity_votes = vec![ValidityAttestation::Explicit(signature)]; + // push the candidate backed_cand.push(BackedCandidate::::new( candidate, validity_votes.clone(), @@ -448,6 +483,7 @@ where &self, dst_inherent_data: &mut sp_inherents::InherentData, ) -> Result<(), sp_inherents::Error> { + // fetch whether the para inherent selector has been set let maybe_para_selector = self .client .get_aux(PARA_INHERENT_SELECTOR_AUX_KEY) @@ -455,6 +491,8 @@ where let inherent_data = { if let Some(aux) = maybe_para_selector { + // if it is true, the candidates need to be mocked + // else, we output the empty parachain inherent data provider if aux == true.encode() { MockParachainsInherentDataProvider::create( self.client.clone(), From 1052f516a5b0b2a13068391eb7ed5f92c3df469f Mon Sep 17 00:00:00 2001 From: girazoki Date: Tue, 19 Nov 2024 15:59:48 +0100 Subject: [PATCH 13/14] pr comm --- solo-chains/node/tanssi-relay-service/src/dev_service.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/src/dev_service.rs b/solo-chains/node/tanssi-relay-service/src/dev_service.rs index 7b4a93372..b8c355b66 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_service.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_service.rs @@ -972,11 +972,11 @@ fn payload_data( out } -/// Create an `AvailabilityBitfield` where `concluding` is a map where each key is a core index -/// that is concluding and `cores` is the total number of cores in the system. -fn availability_bitvec(used_cores: usize, cores: usize) -> AvailabilityBitfield { +/// Create an `AvailabilityBitfield` with size `total_cores`. The first `used_cores` set to true (occupied), +/// and the remaining to false (available). +fn availability_bitvec(used_cores: usize, total_cores: usize) -> AvailabilityBitfield { let mut bitfields = bitvec::bitvec![u8, bitvec::order::Lsb0; 0; 0]; - for i in 0..cores { + for i in 0..total_cores { if i < used_cores { bitfields.push(true); } else { From 8ad39376dd7ba670eca2e5e2b79e2a651dbb60f3 Mon Sep 17 00:00:00 2001 From: girazoki Date: Thu, 21 Nov 2024 09:20:59 +0100 Subject: [PATCH 14/14] remove logs --- solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs index 49f193a69..264670ca8 100644 --- a/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs +++ b/solo-chains/node/tanssi-relay-service/src/dev_rpcs.rs @@ -47,7 +47,6 @@ pub struct DevRpc { #[jsonrpsee::core::async_trait] impl DevApiServer for DevRpc { async fn enable_para_inherent_candidate(&self) -> RpcResult<()> { - log::info!("entering here"); let mock_para_inherent_channel = self.mock_para_inherent_channel.clone(); // Push the message to the shared channel where it will be queued up // to be injected in to an upcoming block. @@ -56,7 +55,6 @@ impl DevApiServer for DevRpc { .await .map_err(|err| internal_err(err.to_string()))?; - log::info!("SENEDING ENABLE"); Ok(()) }