-
Notifications
You must be signed in to change notification settings - Fork 18
/
inherents.rs
236 lines (212 loc) · 10 KB
/
inherents.rs
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
//! APIs and utilities for working with Substrate's Inherents in Tuxedo based chains.
//!
//! # Substrate inherents
//!
//! Inherents are a Substrate feature that allows block authors to insert some transactions directly
//! into the body of the block. Inherents are similar to pre-runtime digests which allow authors to
//! insert info into the block header. However inherents go in the block body and therefore must be transactions.
//!
//! Classic usecases for inherents are injecting and updating environmental information such as a block timestamp,
//! information about the relay chain (if the current chain is a parachain), or information about who should receive the block reward.
//!
//! In order to allow the runtime to construct such transactions while keeping the cleint opaque, there are special APIs
//! for creating inherents and performing off-chain validation of inherents. That's right, inherents also offer
//! a special API to have their environmental data checked off-chain before the block is executed.
//!
//! # Complexities in UTXO chains
//!
//! In account based systems, the classic way to use an inherent is that the block inserts a transaction providing some data like a timestamp.
//! When the extrinsic executed it, overwrites the previously stored timestamp in a dedicated storage item.
//!
//! In UTXO chains, there are no storage items, and all state is local to a UTXO. This is the case with, for example, the timestamp as well.
//! This means that when the author calls into the runtime with a timestamp, the transaction that is returned must include the correct reference
//! to the UTXO that contained the previous best timestamp. This is the crux of the problem: there is no easy way to know the location of
//! the previous timestamp in the utxo-space from inside the runtime.
//!
//! # Scraping the Parent Block
//!
//! The solution is to provide the entirety of the previous block to the runtime when asking it to construct inherents.
//! This module provides an inherent data provider that does just this. Any Tuxedo runtime that uses inherents (At least ones
//! that update environmental data), needs to include this foundational previous block inherent data provider
//! so that the Tuxedo executive can scrape it to find the output references of the previous inherent transactions.
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_core::H256;
use sp_inherents::{
CheckInherentsResult, InherentData, InherentIdentifier, IsFatalError, MakeFatalError,
};
use sp_runtime::traits::Block as BlockT;
use sp_std::{vec, vec::Vec};
use crate::{types::Transaction, ConstraintChecker, Verifier};
/// An inherent identifier for the Tuxedo parent block inherent
pub const PARENT_INHERENT_IDENTIFIER: InherentIdentifier = *b"prnt_blk";
/// An inherent data provider that inserts the previous block into the inherent data.
/// This data does NOT go into an extrinsic.
#[cfg(feature = "std")]
pub struct ParentBlockInherentDataProvider<Block>(pub Block);
#[cfg(feature = "std")]
impl<B> sp_std::ops::Deref for ParentBlockInherentDataProvider<B> {
type Target = B;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "std")]
#[async_trait::async_trait]
impl<B: BlockT> sp_inherents::InherentDataProvider for ParentBlockInherentDataProvider<B> {
async fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
inherent_data.put_data(PARENT_INHERENT_IDENTIFIER, &self.0)
}
async fn try_handle_error(
&self,
identifier: &InherentIdentifier,
error: &[u8],
) -> Option<Result<(), sp_inherents::Error>> {
if identifier == &PARENT_INHERENT_IDENTIFIER {
println!("UH OH! INHERENT ERROR!!!!!!!!!!!!!!!!!!!!!!");
Some(Err(sp_inherents::Error::Application(Box::from(
String::decode(&mut &error[..]).ok()?,
))))
} else {
None
}
}
}
/// Tuxedo's controlled interface around Substrate's concept of inherents.
///
/// This interface assumes that each inherent will appear exactly once in each block.
/// This will be verified off-chain by nodes before block execution begins.
///
/// This interface is stricter and more structured, and therefore simpler than FRAME's.
/// If you need to do something more powerful (which you probably don't) and you
/// understand exactly how Substrate's block authoring and Tuxedo's piece aggregation works
/// (which you probably don't) you can directly implement the `InherentInternal` trait
/// which is more powerful (and dangerous).
pub trait TuxedoInherent<V, C: ConstraintChecker<V>>: Sized {
type Error: Encode + IsFatalError;
const INHERENT_IDENTIFIER: InherentIdentifier;
/// Create the inherent extrinsic to insert into a block that is being authored locally.
/// The inherent data is supplied by the authoring node.
fn create_inherent(
authoring_inherent_data: &InherentData,
previous_inherent: (Transaction<V, C>, H256),
) -> Transaction<V, C>;
/// Perform off-chain pre-execution checks on the inherent.
/// The inherent data is supplied by the importing node.
/// The inherent data available here is not guaranteed to be the
/// same as what is available at authoring time.
fn check_inherent(
importing_inherent_data: &InherentData,
inherent: Transaction<V, C>,
results: &mut CheckInherentsResult,
);
/// Return the genesis transactions that are required for this inherent.
#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>> {
Vec::new()
}
}
/// Almost identical to TuxedoInherent, but allows returning multiple extrinsics
/// (as aggregate runtimes will need to) and removes the requirement that the generic
/// outer constraint checker be buildable from `Self` so we can implement it for ().
///
/// If you are trying to implement some complex inherent logic that requires the interaction of
/// multiple inherents, or features a variable number of inherents in each block, you might be
/// able to express it by implementing this trait, but such designs are probably too complicated.
/// Think long and hard before implementing this trait directly.
pub trait InherentInternal<V, C: ConstraintChecker<V>>: Sized {
/// Create the inherent extrinsic to insert into a block that is being authored locally.
/// The inherent data is supplied by the authoring node.
fn create_inherents(
authoring_inherent_data: &InherentData,
previous_inherents: Vec<(Transaction<V, C>, H256)>,
) -> Vec<Transaction<V, C>>;
/// Perform off-chain pre-execution checks on the inherents.
/// The inherent data is supplied by the importing node.
/// The inherent data available here is not guaranteed to be the
/// same as what is available at authoring time.
fn check_inherents(
importing_inherent_data: &InherentData,
inherents: Vec<Transaction<V, C>>,
results: &mut CheckInherentsResult,
);
/// Return the genesis transactions that are required for the inherents.
#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>>;
}
/// An adapter to transform structured Tuxedo inherents into the more general and powerful
/// InherentInternal trait.
#[derive(Debug, Default, TypeInfo, Clone, Copy)]
pub struct TuxedoInherentAdapter<T>(T);
impl<V: Verifier, C: ConstraintChecker<V>, T: TuxedoInherent<V, C> + 'static> InherentInternal<V, C>
for TuxedoInherentAdapter<T>
{
fn create_inherents(
authoring_inherent_data: &InherentData,
previous_inherents: Vec<(Transaction<V, C>, H256)>,
) -> Vec<Transaction<V, C>> {
if previous_inherents.len() > 1 {
panic!("Authoring a leaf inherent constraint checker, but multiple previous inherents were supplied.")
}
let previous_inherent = previous_inherents.get(0).cloned();
vec![<T as TuxedoInherent<V, C>>::create_inherent(
authoring_inherent_data,
previous_inherent.expect("Previous inherent exists."),
)]
}
fn check_inherents(
importing_inherent_data: &InherentData,
inherents: Vec<Transaction<V, C>>,
results: &mut CheckInherentsResult,
) {
if inherents.is_empty() {
results
.put_error(
*b"12345678",
&MakeFatalError::from(
"Tuxedo inherent expected exactly one inherent extrinsic but found zero",
),
)
.expect("Should be able to put an error.");
return;
} else if inherents.len() > 1 {
results
.put_error(*b"12345678", &MakeFatalError::from("Tuxedo inherent expected exactly one inherent extrinsic but found multiple"))
.expect("Should be able to put an error.");
return;
}
let inherent = inherents.get(0).expect("Previous inherent exists.").clone();
<T as TuxedoInherent<V, C>>::check_inherent(importing_inherent_data, inherent, results)
}
#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>> {
<T as TuxedoInherent<V, C>>::genesis_transactions()
}
}
impl<V, C: ConstraintChecker<V>> InherentInternal<V, C> for () {
fn create_inherents(
_: &InherentData,
_: Vec<(Transaction<V, C>, H256)>,
) -> Vec<Transaction<V, C>> {
Vec::new()
}
fn check_inherents(
_: &InherentData,
inherents: Vec<Transaction<V, C>>,
_: &mut CheckInherentsResult,
) {
// Inherents should always be empty for this stub implementation. Not just in valid blocks, but as an invariant.
// The way we determined which inherents got here is by matching on the constraint checker.
assert!(
inherents.is_empty(),
"inherent extrinsic was passed to check inherents stub implementation."
)
}
#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>> {
Vec::new()
}
}