sp_partner_chains_consensus_aura/
block_proposal.rs

1use crate::InherentDigest;
2use futures::FutureExt;
3use sp_consensus::{Environment, Proposer};
4use sp_inherents::InherentData;
5use sp_runtime::traits::Block as BlockT;
6use sp_runtime::{Digest, DigestItem};
7use std::future::Future;
8use std::marker::PhantomData;
9use std::time;
10
11/// Proposer factory for PartnerChainsProposer. Allows passing ID: InherentDigest type parameter.
12pub struct PartnerChainsProposerFactory<B: BlockT, E: Environment<B>, ID> {
13	env: E,
14	phantom_data: PhantomData<(B, ID)>,
15}
16
17impl<B: BlockT, E: Environment<B>, ID> PartnerChainsProposerFactory<B, E, ID> {
18	pub fn new(env: E) -> Self {
19		Self { env, phantom_data: PhantomData }
20	}
21}
22
23impl<B: BlockT, E: Environment<B>, ID: InherentDigest + Send + Sync + 'static> Environment<B>
24	for PartnerChainsProposerFactory<B, E, ID>
25{
26	type Proposer = PartnerChainsProposer<B, E::Proposer, ID>;
27	type CreateProposer =
28		Box<dyn Future<Output = Result<Self::Proposer, Self::Error>> + Send + Unpin + 'static>;
29	type Error = <E as Environment<B>>::Error;
30
31	fn init(&mut self, parent_header: &<B as BlockT>::Header) -> Self::CreateProposer {
32		Box::new(self.env.init(parent_header).map(|res| {
33			res.map(|proposer| PartnerChainsProposer::<B, E::Proposer, ID>::new(proposer))
34		}))
35	}
36}
37
38/// Wraps a Proposer. Adds inherent data digest to the original logs and calls wrapped Proposer.
39pub struct PartnerChainsProposer<B: BlockT, P: Proposer<B>, ID: InherentDigest> {
40	pub proposer: P,
41	phantom_data: PhantomData<(B, ID)>,
42}
43
44impl<B: BlockT, P: Proposer<B>, ID: InherentDigest> PartnerChainsProposer<B, P, ID> {
45	pub(crate) fn new(proposer: P) -> Self {
46		Self { proposer, phantom_data: PhantomData }
47	}
48}
49
50impl<B: BlockT, P: Proposer<B>, ID: InherentDigest> Proposer<B>
51	for PartnerChainsProposer<B, P, ID>
52{
53	type Error = <P as Proposer<B>>::Error;
54	type Proposal = <P as Proposer<B>>::Proposal;
55	type ProofRecording = <P as Proposer<B>>::ProofRecording;
56	type Proof = <P as Proposer<B>>::Proof;
57
58	fn propose(
59		self,
60		inherent_data: InherentData,
61		inherent_digests: Digest,
62		max_duration: time::Duration,
63		block_size_limit: Option<usize>,
64	) -> Self::Proposal {
65		let mut logs: Vec<DigestItem> = Vec::from(inherent_digests.logs());
66		// It is a programmatic error to try to propose a block that has inherent data from which declared InherentDigest cannot be created.
67		let mut inherent_logs = ID::from_inherent_data(&inherent_data)
68			.expect("InherentDigest can be created from inherent data");
69		logs.append(&mut inherent_logs);
70		self.proposer
71			.propose(inherent_data, Digest { logs }, max_duration, block_size_limit)
72	}
73}
74
75#[cfg(test)]
76mod tests {
77	use crate::InherentDigest;
78	use crate::block_proposal::PartnerChainsProposer;
79	use futures::future;
80	use sp_consensus::{DisableProofRecording, Proposal, Proposer};
81	use sp_inherents::InherentData;
82	use sp_runtime::generic::Header;
83	use sp_runtime::traits::BlakeTwo256;
84	use sp_runtime::{Digest, DigestItem, OpaqueExtrinsic};
85	use std::error::Error;
86
87	pub type Block = sp_runtime::generic::Block<Header<u32, BlakeTwo256>, OpaqueExtrinsic>;
88
89	fn expected_item() -> DigestItem {
90		DigestItem::Other(vec![1, 3, 3, 7])
91	}
92
93	fn other_item() -> DigestItem {
94		DigestItem::Other(vec![0, 0, 0, 0])
95	}
96
97	struct TestInherentDigest;
98
99	impl InherentDigest for TestInherentDigest {
100		type Value = ();
101
102		fn from_inherent_data(
103			_inherent_data: &InherentData,
104		) -> Result<Vec<DigestItem>, Box<dyn Error + Send + Sync>> {
105			Ok(vec![expected_item()])
106		}
107
108		fn value_from_digest(
109			_digests: &[DigestItem],
110		) -> Result<Self::Value, Box<dyn Error + Send + Sync>> {
111			todo!()
112		}
113	}
114
115	struct TestProposer {
116		expected_digest: Digest,
117	}
118
119	impl Proposer<Block> for TestProposer {
120		type Error = sp_blockchain::Error;
121		type Proposal = future::Ready<Result<Proposal<Block, ()>, sp_blockchain::Error>>;
122		type ProofRecording = DisableProofRecording;
123		type Proof = ();
124
125		fn propose(
126			self,
127			_inherent_data: InherentData,
128			inherent_digests: Digest,
129			_max_duration: std::time::Duration,
130			_block_size_limit: Option<usize>,
131		) -> Self::Proposal {
132			let result = if inherent_digests != self.expected_digest {
133				Err(sp_blockchain::Error::Application(
134					"Inherent digest does not match expected digest".into(),
135				))
136			} else {
137				let block = Block {
138					header: Header {
139						parent_hash: Default::default(),
140						number: 0,
141						state_root: Default::default(),
142						extrinsics_root: Default::default(),
143						digest: Default::default(),
144					},
145					extrinsics: Default::default(),
146				};
147				Ok(Proposal { block, proof: (), storage_changes: Default::default() })
148			};
149			futures::future::ready(result)
150		}
151	}
152
153	#[test]
154	fn inherent_digest_is_appended_to_logs() {
155		let inherent_data = InherentData::new();
156		let inherent_digests = Digest { logs: vec![other_item()] };
157		let test_proposer =
158			TestProposer { expected_digest: Digest { logs: vec![other_item(), expected_item()] } };
159		let proposer: PartnerChainsProposer<Block, TestProposer, TestInherentDigest> =
160			PartnerChainsProposer::new(test_proposer);
161		let proposal = proposer
162			.propose(inherent_data, inherent_digests, std::time::Duration::from_secs(0), None)
163			.into_inner();
164		assert!(proposal.is_ok());
165	}
166}