use async_trait::async_trait;
use polkadot_primitives::{
async_backing, runtime_api::ParachainHost, slashing, ApprovalVotingParams, Block, BlockNumber,
CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex,
CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Header, Id,
InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption,
PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo,
ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature,
};
use sc_client_api::{AuxStore, HeaderBackend};
use sc_transaction_pool_api::OffchainTransactionPoolFactory;
use sp_api::{ApiError, ApiExt, ProvideRuntimeApi};
use sp_authority_discovery::AuthorityDiscoveryApi;
use sp_blockchain::{BlockStatus, Info};
use sp_consensus_babe::{BabeApi, Epoch};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use std::{
collections::{BTreeMap, VecDeque},
sync::Arc,
};
#[async_trait]
pub trait ChainApiBackend: Send + Sync {
async fn header(&self, hash: Hash) -> sp_blockchain::Result<Option<Header>>;
async fn info(&self) -> sp_blockchain::Result<Info<Block>>;
async fn number(
&self,
hash: Hash,
) -> sp_blockchain::Result<Option<<Header as HeaderT>::Number>>;
async fn hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Hash>>;
}
#[async_trait]
impl<T> ChainApiBackend for T
where
T: HeaderBackend<Block>,
{
async fn header(&self, hash: Hash) -> sp_blockchain::Result<Option<Header>> {
HeaderBackend::header(self, hash)
}
async fn info(&self) -> sp_blockchain::Result<Info<Block>> {
Ok(HeaderBackend::info(self))
}
async fn number(
&self,
hash: Hash,
) -> sp_blockchain::Result<Option<<Header as HeaderT>::Number>> {
HeaderBackend::number(self, hash)
}
async fn hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Hash>> {
HeaderBackend::hash(self, number)
}
}
#[async_trait]
pub trait RuntimeApiSubsystemClient {
async fn api_version_parachain_host(&self, at: Hash) -> Result<Option<u32>, ApiError>;
async fn validators(&self, at: Hash) -> Result<Vec<ValidatorId>, ApiError>;
async fn validator_groups(
&self,
at: Hash,
) -> Result<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumber>), ApiError>;
async fn availability_cores(
&self,
at: Hash,
) -> Result<Vec<CoreState<Hash, BlockNumber>>, ApiError>;
async fn persisted_validation_data(
&self,
at: Hash,
para_id: Id,
assumption: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData<Hash, BlockNumber>>, ApiError>;
async fn assumed_validation_data(
&self,
at: Hash,
para_id: Id,
expected_persisted_validation_data_hash: Hash,
) -> Result<Option<(PersistedValidationData<Hash, BlockNumber>, ValidationCodeHash)>, ApiError>;
async fn check_validation_outputs(
&self,
at: Hash,
para_id: Id,
outputs: CandidateCommitments,
) -> Result<bool, ApiError>;
async fn session_index_for_child(&self, at: Hash) -> Result<SessionIndex, ApiError>;
async fn validation_code(
&self,
at: Hash,
para_id: Id,
assumption: OccupiedCoreAssumption,
) -> Result<Option<ValidationCode>, ApiError>;
async fn candidate_pending_availability(
&self,
at: Hash,
para_id: Id,
) -> Result<Option<CommittedCandidateReceipt<Hash>>, ApiError>;
async fn candidate_events(&self, at: Hash) -> Result<Vec<CandidateEvent<Hash>>, ApiError>;
async fn dmq_contents(
&self,
at: Hash,
recipient: Id,
) -> Result<Vec<InboundDownwardMessage<BlockNumber>>, ApiError>;
async fn inbound_hrmp_channels_contents(
&self,
at: Hash,
recipient: Id,
) -> Result<BTreeMap<Id, Vec<InboundHrmpMessage<BlockNumber>>>, ApiError>;
async fn validation_code_by_hash(
&self,
at: Hash,
hash: ValidationCodeHash,
) -> Result<Option<ValidationCode>, ApiError>;
async fn on_chain_votes(&self, at: Hash)
-> Result<Option<ScrapedOnChainVotes<Hash>>, ApiError>;
async fn session_info(
&self,
at: Hash,
index: SessionIndex,
) -> Result<Option<SessionInfo>, ApiError>;
async fn submit_pvf_check_statement(
&self,
at: Hash,
stmt: PvfCheckStatement,
signature: ValidatorSignature,
) -> Result<(), ApiError>;
async fn pvfs_require_precheck(&self, at: Hash) -> Result<Vec<ValidationCodeHash>, ApiError>;
async fn validation_code_hash(
&self,
at: Hash,
para_id: Id,
assumption: OccupiedCoreAssumption,
) -> Result<Option<ValidationCodeHash>, ApiError>;
async fn disputes(
&self,
at: Hash,
) -> Result<Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>, ApiError>;
async fn unapplied_slashes(
&self,
at: Hash,
) -> Result<Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)>, ApiError>;
async fn key_ownership_proof(
&self,
at: Hash,
validator_id: ValidatorId,
) -> Result<Option<slashing::OpaqueKeyOwnershipProof>, ApiError>;
async fn submit_report_dispute_lost(
&self,
at: Hash,
dispute_proof: slashing::DisputeProof,
key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
) -> Result<Option<()>, ApiError>;
async fn current_epoch(&self, at: Hash) -> Result<Epoch, ApiError>;
async fn authorities(
&self,
at: Hash,
) -> std::result::Result<Vec<sp_authority_discovery::AuthorityId>, ApiError>;
async fn session_executor_params(
&self,
at: Hash,
session_index: SessionIndex,
) -> Result<Option<ExecutorParams>, ApiError>;
async fn minimum_backing_votes(
&self,
at: Hash,
session_index: SessionIndex,
) -> Result<u32, ApiError>;
async fn async_backing_params(
&self,
at: Hash,
) -> Result<polkadot_primitives::AsyncBackingParams, ApiError>;
async fn para_backing_state(
&self,
at: Hash,
para_id: Id,
) -> Result<Option<async_backing::BackingState>, ApiError>;
async fn disabled_validators(&self, at: Hash) -> Result<Vec<ValidatorIndex>, ApiError>;
async fn node_features(&self, at: Hash) -> Result<NodeFeatures, ApiError>;
async fn approval_voting_params(
&self,
at: Hash,
session_index: SessionIndex,
) -> Result<ApprovalVotingParams, ApiError>;
async fn claim_queue(&self, at: Hash) -> Result<BTreeMap<CoreIndex, VecDeque<Id>>, ApiError>;
async fn candidates_pending_availability(
&self,
at: Hash,
para_id: Id,
) -> Result<Vec<CommittedCandidateReceipt<Hash>>, ApiError>;
}
pub struct DefaultSubsystemClient<Client> {
client: Arc<Client>,
offchain_transaction_pool_factory: OffchainTransactionPoolFactory<Block>,
}
impl<Client> DefaultSubsystemClient<Client> {
pub fn new(
client: Arc<Client>,
offchain_transaction_pool_factory: OffchainTransactionPoolFactory<Block>,
) -> Self {
Self { client, offchain_transaction_pool_factory }
}
}
#[async_trait]
impl<Client> RuntimeApiSubsystemClient for DefaultSubsystemClient<Client>
where
Client: ProvideRuntimeApi<Block> + Send + Sync,
Client::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>,
{
async fn validators(&self, at: Hash) -> Result<Vec<ValidatorId>, ApiError> {
self.client.runtime_api().validators(at)
}
async fn validator_groups(
&self,
at: Hash,
) -> Result<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumber>), ApiError> {
self.client.runtime_api().validator_groups(at)
}
async fn availability_cores(
&self,
at: Hash,
) -> Result<Vec<CoreState<Hash, BlockNumber>>, ApiError> {
self.client
.runtime_api()
.availability_cores(at)
.map(|cores| cores.into_iter().map(|core| core.into()).collect::<Vec<_>>())
}
async fn persisted_validation_data(
&self,
at: Hash,
para_id: Id,
assumption: OccupiedCoreAssumption,
) -> Result<Option<PersistedValidationData<Hash, BlockNumber>>, ApiError> {
self.client.runtime_api().persisted_validation_data(at, para_id, assumption)
}
async fn assumed_validation_data(
&self,
at: Hash,
para_id: Id,
expected_persisted_validation_data_hash: Hash,
) -> Result<Option<(PersistedValidationData<Hash, BlockNumber>, ValidationCodeHash)>, ApiError>
{
self.client.runtime_api().assumed_validation_data(
at,
para_id,
expected_persisted_validation_data_hash,
)
}
async fn check_validation_outputs(
&self,
at: Hash,
para_id: Id,
outputs: CandidateCommitments,
) -> Result<bool, ApiError> {
self.client.runtime_api().check_validation_outputs(at, para_id, outputs)
}
async fn session_index_for_child(&self, at: Hash) -> Result<SessionIndex, ApiError> {
self.client.runtime_api().session_index_for_child(at)
}
async fn validation_code(
&self,
at: Hash,
para_id: Id,
assumption: OccupiedCoreAssumption,
) -> Result<Option<ValidationCode>, ApiError> {
self.client.runtime_api().validation_code(at, para_id, assumption)
}
async fn candidate_pending_availability(
&self,
at: Hash,
para_id: Id,
) -> Result<Option<CommittedCandidateReceipt<Hash>>, ApiError> {
self.client
.runtime_api()
.candidate_pending_availability(at, para_id)
.map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into()))
}
async fn candidates_pending_availability(
&self,
at: Hash,
para_id: Id,
) -> Result<Vec<CommittedCandidateReceipt<Hash>>, ApiError> {
self.client
.runtime_api()
.candidates_pending_availability(at, para_id)
.map(|candidates| {
candidates.into_iter().map(|candidate| candidate.into()).collect::<Vec<_>>()
})
}
async fn candidate_events(&self, at: Hash) -> Result<Vec<CandidateEvent<Hash>>, ApiError> {
self.client
.runtime_api()
.candidate_events(at)
.map(|events| events.into_iter().map(|event| event.into()).collect::<Vec<_>>())
}
async fn dmq_contents(
&self,
at: Hash,
recipient: Id,
) -> Result<Vec<InboundDownwardMessage<BlockNumber>>, ApiError> {
self.client.runtime_api().dmq_contents(at, recipient)
}
async fn inbound_hrmp_channels_contents(
&self,
at: Hash,
recipient: Id,
) -> Result<BTreeMap<Id, Vec<InboundHrmpMessage<BlockNumber>>>, ApiError> {
self.client.runtime_api().inbound_hrmp_channels_contents(at, recipient)
}
async fn validation_code_by_hash(
&self,
at: Hash,
hash: ValidationCodeHash,
) -> Result<Option<ValidationCode>, ApiError> {
self.client.runtime_api().validation_code_by_hash(at, hash)
}
async fn on_chain_votes(
&self,
at: Hash,
) -> Result<Option<ScrapedOnChainVotes<Hash>>, ApiError> {
self.client
.runtime_api()
.on_chain_votes(at)
.map(|maybe_votes| maybe_votes.map(|votes| votes.into()))
}
async fn session_executor_params(
&self,
at: Hash,
session_index: SessionIndex,
) -> Result<Option<ExecutorParams>, ApiError> {
self.client.runtime_api().session_executor_params(at, session_index)
}
async fn session_info(
&self,
at: Hash,
index: SessionIndex,
) -> Result<Option<SessionInfo>, ApiError> {
self.client.runtime_api().session_info(at, index)
}
async fn submit_pvf_check_statement(
&self,
at: Hash,
stmt: PvfCheckStatement,
signature: ValidatorSignature,
) -> Result<(), ApiError> {
let mut runtime_api = self.client.runtime_api();
runtime_api.register_extension(
self.offchain_transaction_pool_factory.offchain_transaction_pool(at),
);
runtime_api.submit_pvf_check_statement(at, stmt, signature)
}
async fn pvfs_require_precheck(&self, at: Hash) -> Result<Vec<ValidationCodeHash>, ApiError> {
self.client.runtime_api().pvfs_require_precheck(at)
}
async fn validation_code_hash(
&self,
at: Hash,
para_id: Id,
assumption: OccupiedCoreAssumption,
) -> Result<Option<ValidationCodeHash>, ApiError> {
self.client.runtime_api().validation_code_hash(at, para_id, assumption)
}
async fn current_epoch(&self, at: Hash) -> Result<Epoch, ApiError> {
self.client.runtime_api().current_epoch(at)
}
async fn authorities(
&self,
at: Hash,
) -> std::result::Result<Vec<sp_authority_discovery::AuthorityId>, ApiError> {
self.client.runtime_api().authorities(at)
}
async fn api_version_parachain_host(&self, at: Hash) -> Result<Option<u32>, ApiError> {
self.client.runtime_api().api_version::<dyn ParachainHost<Block>>(at)
}
async fn disputes(
&self,
at: Hash,
) -> Result<Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>, ApiError> {
self.client.runtime_api().disputes(at)
}
async fn unapplied_slashes(
&self,
at: Hash,
) -> Result<Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)>, ApiError> {
self.client.runtime_api().unapplied_slashes(at)
}
async fn key_ownership_proof(
&self,
at: Hash,
validator_id: ValidatorId,
) -> Result<Option<slashing::OpaqueKeyOwnershipProof>, ApiError> {
self.client.runtime_api().key_ownership_proof(at, validator_id)
}
async fn submit_report_dispute_lost(
&self,
at: Hash,
dispute_proof: slashing::DisputeProof,
key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
) -> Result<Option<()>, ApiError> {
let mut runtime_api = self.client.runtime_api();
runtime_api.register_extension(
self.offchain_transaction_pool_factory.offchain_transaction_pool(at),
);
runtime_api.submit_report_dispute_lost(at, dispute_proof, key_ownership_proof)
}
async fn minimum_backing_votes(
&self,
at: Hash,
_session_index: SessionIndex,
) -> Result<u32, ApiError> {
self.client.runtime_api().minimum_backing_votes(at)
}
async fn para_backing_state(
&self,
at: Hash,
para_id: Id,
) -> Result<Option<async_backing::BackingState>, ApiError> {
self.client
.runtime_api()
.para_backing_state(at, para_id)
.map(|maybe_backing_state| {
maybe_backing_state.map(|backing_state| backing_state.into())
})
}
async fn async_backing_params(
&self,
at: Hash,
) -> Result<async_backing::AsyncBackingParams, ApiError> {
self.client.runtime_api().async_backing_params(at)
}
async fn node_features(&self, at: Hash) -> Result<NodeFeatures, ApiError> {
self.client.runtime_api().node_features(at)
}
async fn disabled_validators(&self, at: Hash) -> Result<Vec<ValidatorIndex>, ApiError> {
self.client.runtime_api().disabled_validators(at)
}
async fn approval_voting_params(
&self,
at: Hash,
_session_index: SessionIndex,
) -> Result<ApprovalVotingParams, ApiError> {
self.client.runtime_api().approval_voting_params(at)
}
async fn claim_queue(&self, at: Hash) -> Result<BTreeMap<CoreIndex, VecDeque<Id>>, ApiError> {
self.client.runtime_api().claim_queue(at)
}
}
impl<Client, Block> HeaderBackend<Block> for DefaultSubsystemClient<Client>
where
Client: HeaderBackend<Block>,
Block: sp_runtime::traits::Block,
{
fn header(
&self,
hash: Block::Hash,
) -> sc_client_api::blockchain::Result<Option<Block::Header>> {
self.client.header(hash)
}
fn info(&self) -> Info<Block> {
self.client.info()
}
fn status(&self, hash: Block::Hash) -> sc_client_api::blockchain::Result<BlockStatus> {
self.client.status(hash)
}
fn number(
&self,
hash: Block::Hash,
) -> sc_client_api::blockchain::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>>
{
self.client.number(hash)
}
fn hash(
&self,
number: NumberFor<Block>,
) -> sc_client_api::blockchain::Result<Option<Block::Hash>> {
self.client.hash(number)
}
}
impl<Client> AuxStore for DefaultSubsystemClient<Client>
where
Client: AuxStore,
{
fn insert_aux<
'a,
'b: 'a,
'c: 'a,
I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
D: IntoIterator<Item = &'a &'b [u8]>,
>(
&self,
insert: I,
delete: D,
) -> sp_blockchain::Result<()> {
self.client.insert_aux(insert, delete)
}
fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
self.client.get_aux(key)
}
}