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()
}