@@ -21,8 +21,9 @@ use parity_scale_codec::{Decode, Encode};
21
21
use scale_info:: TypeInfo ;
22
22
use sp_consensus_slots:: Slot ;
23
23
use sp_core:: H256 ;
24
- use sp_runtime:: traits:: { BlakeTwo256 , Hash as HashT } ;
24
+ use sp_runtime:: traits:: { BlakeTwo256 , Hash as HashT , Header as HeaderT } ;
25
25
use sp_runtime:: { OpaqueExtrinsic , RuntimeDebug } ;
26
+ use sp_runtime_interface:: pass_by:: PassBy ;
26
27
use sp_std:: borrow:: Cow ;
27
28
use sp_std:: vec:: Vec ;
28
29
use sp_trie:: StorageProof ;
@@ -125,15 +126,112 @@ impl<Hash: Encode> From<ExecutionReceipt<Hash>> for OpaqueExecutionReceipt {
125
126
}
126
127
}
127
128
129
+ /// Execution phase along with an optional encoded call data.
130
+ ///
131
+ /// Each execution phase has a different method for the runtime call.
132
+ #[ derive( Decode , Encode , TypeInfo , PartialEq , Eq , Clone , RuntimeDebug ) ]
133
+ pub enum ExecutionPhase {
134
+ /// Executes the `initialize_block` hook.
135
+ InitializeBlock { call_data : Vec < u8 > } ,
136
+ /// Executes some extrinsic.
137
+ /// TODO: maybe optimized to not include the whole extrinsic blob in the future.
138
+ ApplyExtrinsic { call_data : Vec < u8 > } ,
139
+ /// Executes the `finalize_block` hook.
140
+ FinalizeBlock ,
141
+ }
142
+
143
+ impl ExecutionPhase {
144
+ /// Returns the method for generating the proof.
145
+ pub fn proving_method ( & self ) -> & ' static str {
146
+ match self {
147
+ // TODO: Replace `SecondaryApi_initialize_block_with_post_state_root` with `Core_initalize_block`
148
+ // Should be a same issue with https://github.com/paritytech/substrate/pull/10922#issuecomment-1068997467
149
+ Self :: InitializeBlock { .. } => "SecondaryApi_initialize_block_with_post_state_root" ,
150
+ Self :: ApplyExtrinsic { .. } => "BlockBuilder_apply_extrinsic" ,
151
+ Self :: FinalizeBlock => "BlockBuilder_finalize_block" ,
152
+ }
153
+ }
154
+
155
+ /// Returns the method for verifying the proof.
156
+ ///
157
+ /// The difference with [`Self::proving_method`] is that the return value of verifying method
158
+ /// must contain the post state root info so that it can be used to compare whether the
159
+ /// result of execution reported in [`FraudProof`] is expected or not.
160
+ pub fn verifying_method ( & self ) -> & ' static str {
161
+ match self {
162
+ Self :: InitializeBlock { .. } => "SecondaryApi_initialize_block_with_post_state_root" ,
163
+ Self :: ApplyExtrinsic { .. } => "SecondaryApi_apply_extrinsic_with_post_state_root" ,
164
+ Self :: FinalizeBlock => "BlockBuilder_finalize_block" ,
165
+ }
166
+ }
167
+
168
+ /// Returns the call data used to generate and verify the proof.
169
+ pub fn call_data ( & self ) -> & [ u8 ] {
170
+ match self {
171
+ Self :: InitializeBlock { call_data } | Self :: ApplyExtrinsic { call_data } => call_data,
172
+ Self :: FinalizeBlock => Default :: default ( ) ,
173
+ }
174
+ }
175
+
176
+ /// Returns the post state root for the given execution result.
177
+ pub fn decode_execution_result < Header : HeaderT > (
178
+ & self ,
179
+ execution_result : Vec < u8 > ,
180
+ ) -> Result < Header :: Hash , VerificationError > {
181
+ match self {
182
+ ExecutionPhase :: InitializeBlock { .. } | ExecutionPhase :: ApplyExtrinsic { .. } => {
183
+ let encoded_storage_root = Vec :: < u8 > :: decode ( & mut execution_result. as_slice ( ) )
184
+ . map_err ( VerificationError :: InitializeBlockOrApplyExtrinsicDecode ) ?;
185
+ Header :: Hash :: decode ( & mut encoded_storage_root. as_slice ( ) )
186
+ . map_err ( VerificationError :: StorageRootDecode )
187
+ }
188
+ ExecutionPhase :: FinalizeBlock => {
189
+ let new_header = Header :: decode ( & mut execution_result. as_slice ( ) )
190
+ . map_err ( VerificationError :: HeaderDecode ) ?;
191
+ Ok ( * new_header. state_root ( ) )
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ /// Error type of fraud proof verification on primary node.
198
+ #[ derive( RuntimeDebug ) ]
199
+ pub enum VerificationError {
200
+ /// Runtime code backend unavailable.
201
+ RuntimeCodeBackend ,
202
+ /// Runtime code can not be fetched from the backend.
203
+ RuntimeCode ( & ' static str ) ,
204
+ /// Failed to pass the execution proof check.
205
+ BadProof ( sp_std:: boxed:: Box < dyn sp_state_machine:: Error > ) ,
206
+ /// The `post_state_root` calculated by farmer does not match the one declared in [`FraudProof`].
207
+ BadPostStateRoot { expected : H256 , got : H256 } ,
208
+ /// Failed to decode the return value of `initialize_block` and `apply_extrinsic`.
209
+ InitializeBlockOrApplyExtrinsicDecode ( parity_scale_codec:: Error ) ,
210
+ /// Failed to decode the storage root produced by verifying `initialize_block` or `apply_extrinsic`.
211
+ StorageRootDecode ( parity_scale_codec:: Error ) ,
212
+ /// Failed to decode the header produced by `finalize_block`.
213
+ HeaderDecode ( parity_scale_codec:: Error ) ,
214
+ }
215
+
128
216
/// Fraud proof for the state computation.
129
217
#[ derive( Decode , Encode , TypeInfo , PartialEq , Eq , Clone , RuntimeDebug ) ]
130
218
pub struct FraudProof {
219
+ /// Parent hash of the block at which the invalid execution occurred.
220
+ ///
221
+ /// Runtime code for this block's execution is retrieved on top of the parent block.
222
+ pub parent_hash : H256 ,
131
223
/// State root before the fraudulent transaction.
132
224
pub pre_state_root : H256 ,
133
225
/// State root after the fraudulent transaction.
134
226
pub post_state_root : H256 ,
135
227
/// Proof recorded during the computation.
136
228
pub proof : StorageProof ,
229
+ /// Execution phase.
230
+ pub execution_phase : ExecutionPhase ,
231
+ }
232
+
233
+ impl PassBy for FraudProof {
234
+ type PassBy = sp_runtime_interface:: pass_by:: Codec < Self > ;
137
235
}
138
236
139
237
/// Represents a bundle equivocation proof. An equivocation happens when an executor
@@ -213,3 +311,37 @@ sp_api::decl_runtime_apis! {
213
311
fn execution_wasm_bundle( ) -> Cow <' static , [ u8 ] >;
214
312
}
215
313
}
314
+
315
+ pub mod fraud_proof_ext {
316
+ use sp_externalities:: ExternalitiesExt ;
317
+ use sp_runtime_interface:: runtime_interface;
318
+
319
+ /// Externalities for verifying fraud proof.
320
+ pub trait Externalities : Send {
321
+ /// Returns `true` when the proof is valid.
322
+ fn verify_fraud_proof ( & self , proof : & crate :: FraudProof ) -> bool ;
323
+ }
324
+
325
+ #[ cfg( feature = "std" ) ]
326
+ sp_externalities:: decl_extension! {
327
+ /// An extension to verify the fraud proof.
328
+ pub struct FraudProofExt ( Box <dyn Externalities >) ;
329
+ }
330
+
331
+ #[ cfg( feature = "std" ) ]
332
+ impl FraudProofExt {
333
+ pub fn new < E : Externalities + ' static > ( fraud_proof : E ) -> Self {
334
+ Self ( Box :: new ( fraud_proof) )
335
+ }
336
+ }
337
+
338
+ #[ runtime_interface]
339
+ pub trait FraudProof {
340
+ /// Verify fraud proof.
341
+ fn verify ( & mut self , proof : & crate :: FraudProof ) -> bool {
342
+ self . extension :: < FraudProofExt > ( )
343
+ . expect ( "No `FraudProof` associated for the current context!" )
344
+ . verify_fraud_proof ( proof)
345
+ }
346
+ }
347
+ }
0 commit comments