1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
//! Allow collators to include information about the relay chain, parachain, and their relationship, via an inherent.
//!
//! This piece is necessary if the runtime is going to work as a parachain.
//!
//! In each block, the block author must include a single `SetParachainInfo` transaction that consumes the
//! corresponding UTXO that was created in the previous block, and creates a new one with updated parachain info.
//! This is quite similar to how the timestamp inherent works, except that in this case we are consuming the previous
//! input directly instead of peeking. This decision may be revisitied if keeping the info around would be useful.
//!
//! ## Comparison with Cumulus Pallet Parachain System
//!
//! This is similar to FRAME's pallet parachain system, although this piece is only responsible for the inherent flow
//! while that pallet is responsible for most of the core parachain requirements including the validate block function.
//!
//! ## Hack Warning
//!
//! Like the timestamp piece, this piece currently abuses the UpForGrabs verifier.
//! This should be replaced with an Unspendable verifier and an eviction workflow.
#![cfg_attr(not(feature = "std"), no_std)]
use core::marker::PhantomData;
pub use cumulus_primitives_parachain_inherent::ParachainInherentData;
use cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use sp_core::H256;
use sp_inherents::{CheckInherentsResult, InherentData};
use sp_runtime::transaction_validity::TransactionPriority;
use sp_std::{vec, vec::Vec};
use tuxedo_parachain_core::tuxedo_core::dynamic_typing::DynamicallyTypedData;
use tuxedo_parachain_core::tuxedo_core::SimpleConstraintChecker;
// We get all the Tuxedo core stuff through the re-export so we don't risk crossed versions.
use tuxedo_parachain_core::ParachainInherentDataUtxo;
use tuxedo_parachain_core::{
tuxedo_core::{
ensure,
inherents::InherentHooks,
support_macros::{CloneNoBound, DebugNoBound, DefaultNoBound},
types::{Input, Output, OutputRef, RedemptionStrategy, Transaction},
Verifier,
},
SetRelayParentNumberStorage,
};
#[cfg(test)]
mod tests;
/// A piece-wide target for logging
const LOG_TARGET: &str = "parachain-info";
/// Options to configure the timestamp piece when it is aggregated or used in a runtime.
pub trait ParachainPieceConfig {
// This value is duplicated in the chain spec extension. Soon (as soon as SDK 1.4.0) the API
// for genesis storage is being refactored to have access to the chain spec, and this design
// should be revised. We will likely want to leverage storage similar to how we have
// for the relay chain parent id.
/// The Parachain Id of this chain.
/// This is currently a copmile time constant, which is simple but not that flexible.
///
/// The default value is set to 2_000 to match the first available id in the rococo-local runtime.
const PARA_ID: u32 = 2_000;
/// A means of setting an ambiently available relay parent number. This value WILL be used when
/// the collator calls the collation API after the block is authored and also in validate_block.
/// Additionally, it MAY be used by any other pieces in the runtime who have access to it.
type SetRelayParentNumberStorage: SetRelayParentNumberStorage;
}
/// Reasons that setting or cleaning up the parachain info may go wrong.
#[derive(Debug, Eq, PartialEq)]
pub enum ParachainError {
/// UTXO data has an unexpected type
BadlyTyped,
/// When attempting to set a new parachain info, you have not included any output.
MissingNewInfo,
/// Multiple outputs were specified while setting the parachain info, but exactly one is required.
ExtraOutputs,
/// No previous parachain info was consumed in this transaction, but consuming the previous UTXO is required.
MissingPreviousInfo,
/// Multiple inputs were specified while setting the parachain info, but exactly one is required.
ExtraInputs,
/// The new relay chain block number is expected to be higher than the previous, but that is not the case.
RelayBlockNotIncreasing,
}
/// A constraint checker for the simple act of including new parachain information.
///
/// This is expected to be performed through an inherent, and to happen exactly once per block.
///
/// This transaction comsumes a single input which is the previous parachain info,
/// And it creates a new output which is the current parachain info.
#[derive(
Serialize,
Deserialize,
Encode,
Decode,
DebugNoBound,
DefaultNoBound,
PartialEq,
Eq,
CloneNoBound,
TypeInfo,
)]
#[scale_info(skip_type_params(T))]
pub struct SetParachainInfo<T>(PhantomData<T>);
impl<T: ParachainPieceConfig + 'static> SimpleConstraintChecker for SetParachainInfo<T> {
type Error = ParachainError;
fn check(
&self,
input_data: &[DynamicallyTypedData],
evicted_input_data: &[DynamicallyTypedData],
_peek_data: &[DynamicallyTypedData],
output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, Self::Error> {
log::debug!(
target: LOG_TARGET,
"Checking onchain constraints for SetParachainInfo."
);
// Make sure there is exactly one output which is the current parachain info
ensure!(!output_data.is_empty(), Self::Error::MissingNewInfo);
ensure!(output_data.len() == 1, Self::Error::ExtraOutputs);
let current: ParachainInherentData = output_data[0]
.extract::<ParachainInherentDataUtxo>()
.map_err(|_| Self::Error::BadlyTyped)?
.into();
// SIDE EFFECT: Write the relay parent block number to storage to use later in the collation info api
T::SetRelayParentNumberStorage::set(current.validation_data.relay_parent_number);
// Make sure there is exactly one EVICTED input which is the previous parachain info,
// and no normal inputs
ensure!(input_data.is_empty(), Self::Error::ExtraInputs);
ensure!(
!evicted_input_data.is_empty(),
Self::Error::MissingPreviousInfo
);
ensure!(evicted_input_data.len() == 1, Self::Error::ExtraInputs);
let previous: ParachainInherentData = evicted_input_data[0]
.extract::<ParachainInherentDataUtxo>()
.map_err(|_| Self::Error::BadlyTyped)?
.into();
// Make sure the relay chain block height is strictly increasing.
// In frame this logic is generic and it doesn't have to be so strict.
// But for now I'll start simple.
ensure!(
current.validation_data.relay_parent_number
> previous.validation_data.relay_parent_number,
Self::Error::RelayBlockNotIncreasing,
);
// We may need to put a log on the block header at some point.
// Frame does this. However, it seems this design is not fully fleshed out in cumulus itself.
// FIXME https://github.com/Off-Narrative-Labs/Tuxedo/issues/147 for more context and info.
// When we support async backing, we will need to validate that the parent is included
// and perform consensus checks as well. For now, we rely on synchronous backing.
// FIXME https://github.com/Off-Narrative-Labs/Tuxedo/issues/148
Ok(0)
}
}
impl<T: ParachainPieceConfig + 'static> InherentHooks for SetParachainInfo<T> {
// Same error type as in frame
type Error = sp_inherents::MakeFatalError<()>;
const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = INHERENT_IDENTIFIER;
fn create_inherent<V: Verifier>(
authoring_inherent_data: &InherentData,
(_previous_inherent, previous_id): (Transaction<V, Self>, H256),
) -> Transaction<V, Self> {
let current_info: ParachainInherentData = authoring_inherent_data
.get_data(&INHERENT_IDENTIFIER)
.expect("Inherent data should decode properly")
.expect("Parachain inherent data should be present.");
log::debug!(
target: LOG_TARGET,
"parachain inherent data while creating inherent: {:?}", current_info
);
// The first task is to construct the input that we will consume.
// We are given the entire previous inherent in case we need data from it or need to scrape the outputs.
// But our transactions are simple enough that we know we just need the one and only output.
let output_ref = OutputRef {
tx_hash: previous_id,
// There is always 1 output, so we know right where to find it.
index: 0,
};
let input = Input {
output_ref,
redeemer: RedemptionStrategy::Eviction,
};
let new_output = Output {
payload: ParachainInherentDataUtxo::from(current_info).into(),
verifier: V::new_unspendable()
.expect("Must be able to create unspendable verifier to use parachain inherent."),
};
let t = Transaction {
inputs: vec![input],
peeks: Vec::new(),
outputs: vec![new_output],
checker: Self::default(),
};
log::debug!(
target: LOG_TARGET,
"created inherent transaction {:?}.", t
);
t
}
fn check_inherent<V>(
_importing_inherent_data: &InherentData,
_inherent: Transaction<V, Self>,
_result: &mut CheckInherentsResult,
) {
log::debug!(
target: LOG_TARGET,
"In check_inherents for parachain inherent. No actual off-chain checks are required."
);
//TODO The debug message above reflects the current design which is a copy of basti's design.
// I think the process of checking this inherent should be accessible through some abstract interface in the end.
}
#[cfg(feature = "std")]
fn genesis_transactions<V: Verifier>() -> Vec<Transaction<V, Self>> {
let payload = new_data_from_relay_parent_number(0).into();
vec![Transaction {
inputs: Vec::new(),
peeks: Vec::new(),
outputs: vec![Output {
payload,
verifier: V::new_unspendable().expect(
"Must be able to create unspendable verifier to use timestamp inherent.",
),
}],
checker: Self::default(),
}]
}
}
#[cfg(feature = "std")]
fn new_data_from_relay_parent_number(relay_parent_number: u32) -> ParachainInherentDataUtxo {
let sproof_builder = cumulus_test_relay_sproof_builder::RelayStateSproofBuilder::default();
//TODO consider changing the para_id here. For sure do it if we keep the piece config item.
let (relay_parent_storage_root, relay_chain_state_proof) =
sproof_builder.into_state_root_and_proof();
ParachainInherentData {
validation_data: cumulus_primitives_core::PersistedValidationData {
parent_head: Default::default(),
relay_parent_number,
relay_parent_storage_root,
max_pov_size: Default::default(),
},
relay_chain_state: relay_chain_state_proof,
downward_messages: Default::default(),
horizontal_messages: Default::default(),
}
.into()
}