1use crate::pallas_applying::{
9 babbage::{
10 check_ins_not_empty,
11 check_preservation_of_value,
13 check_tx_validity_interval,
14 check_witness_set,
15 },
16 utils::BabbageError::*,
17 UTxOs,
18};
19use crate::pallas_codec::utils::CborWrap;
20use crate::pallas_primitives::{
21 babbage::{
22 MintedDatumOption, MintedScriptRef, MintedTransactionBody, MintedTx,
23 Tx as PallasTransaction, Value as PallasValue,
24 },
25 conway::{MintedTx as ConwayMintedTx, TransactionOutput},
26};
27use crate::uplc::tx::{eval_phase_two, ResolvedInput, SlotConfig};
28use crate::{
29 checks_interface::{
30 babbage_minted_tx_from_cbor, babbage_tx_to_cbor, check_min_coin,
31 conway_minted_tx_from_cbor, mk_utxo_for_babbage_tx,
32 },
33 ensure,
34 types::{Block, BlockNumber, DispatchResult, Header, Input, Output, Transaction, UTxOError},
35 utxo_set::TransparentUtxoSet,
36 EXTRINSIC_KEY, HEADER_KEY, HEIGHT_KEY, LOG_TARGET,
37};
38use crate::{MILLI_SECS_PER_SLOT, ZERO_SLOT, ZERO_TIME};
39use alloc::{collections::btree_set::BTreeSet, string::String, vec::Vec};
40use log::debug;
41use parity_scale_codec::{Decode, Encode};
42use sp_runtime::{
43 traits::{BlakeTwo256, Block as BlockT, Extrinsic, Hash as HashT, Header as HeaderT},
44 transaction_validity::{
45 TransactionLongevity, TransactionSource, TransactionValidity, TransactionValidityError,
46 ValidTransaction,
47 },
48 ApplyExtrinsicResult, ExtrinsicInclusionMode, StateVersion,
49};
50
51type OutputInfoList<'a> = Vec<(
52 String, PallasValue,
54 Option<MintedDatumOption<'a>>,
55 Option<CborWrap<MintedScriptRef<'a>>>,
56)>;
57
58pub struct Executive;
62
63impl Executive
64where
65 Block: BlockT,
66 Transaction: Extrinsic,
67{
68 fn pool_checks(mtx: &MintedTx, _utxos: &UTxOs) -> DispatchResult {
71 check_ins_not_empty(&mtx.transaction_body.clone())?;
72 Ok(())
73 }
74
75 fn ledger_checks(mtx: &MintedTx, utxos: &UTxOs) -> DispatchResult {
78 let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone();
79 let current_slot = Self::zero_slot() + (Self::block_height() as u64);
82 check_tx_validity_interval(tx_body, ¤t_slot)?;
83 check_preservation_of_value(tx_body, utxos)?;
84 check_witness_set(mtx, utxos)?;
85 check_min_coin(tx_body)?;
86
87 Ok(())
88 }
89
90 fn phase_two_checks(tx_cbor_bytes: &Vec<u8>, input_utxos: Vec<Output>) -> DispatchResult {
91 let conway_mtx: ConwayMintedTx = conway_minted_tx_from_cbor(&tx_cbor_bytes);
92 let pallas_input_utxos = input_utxos
93 .iter()
94 .map(|ri| TransactionOutput::from(ri.clone()))
95 .collect::<Vec<_>>();
96 let pallas_resolved_inputs: Vec<ResolvedInput> = conway_mtx
97 .transaction_body
98 .inputs
99 .iter()
100 .zip(pallas_input_utxos.iter())
101 .map(|(input, output)| ResolvedInput {
102 input: input.clone(),
103 output: output.clone(),
104 })
105 .collect();
106
107 let slot_config = SlotConfig {
108 zero_time: Self::zero_time(),
109 zero_slot: Self::zero_slot(),
110 slot_length: MILLI_SECS_PER_SLOT,
111 };
112 let phase_two_result = eval_phase_two(
113 &conway_mtx,
114 &pallas_resolved_inputs,
115 None,
116 None,
117 &slot_config,
118 false,
119 |_| (),
120 );
121 ensure!(
122 phase_two_result.is_ok(),
123 UTxOError::PhaseTwo(phase_two_result.unwrap_err())
124 );
125
126 Ok(())
127 }
128
129 fn validate_griffin_transaction(
137 transaction: &Transaction,
138 ) -> Result<ValidTransaction, UTxOError> {
139 debug!(
140 target: LOG_TARGET,
141 "validating griffin transaction",
142 );
143
144 {
146 let input_set: BTreeSet<_> = transaction
147 .transaction_body
148 .inputs
149 .iter()
150 .map(|o| o.encode())
151 .collect();
152 ensure!(
153 input_set.len() == transaction.transaction_body.inputs.len(),
154 UTxOError::Babbage(DuplicateInput)
155 );
156 }
157
158 let mut tx_outs_info: OutputInfoList = Vec::new();
159 let mut input_utxos: Vec<Output> = Vec::new();
160
161 let mut missing_inputs = Vec::new();
164 for input in transaction.transaction_body.inputs.iter() {
165 if let Some(u) = TransparentUtxoSet::peek_utxo(&input) {
166 tx_outs_info.push((
167 hex::encode(u.address.0.as_slice()),
168 PallasValue::from(u.clone().value),
169 None, None,
171 ));
172 input_utxos.push(u);
174 } else {
175 missing_inputs.push(input.clone().encode());
176 }
177 }
178
179 let tx_hash = BlakeTwo256::hash_of(&transaction.encode());
181 for index in 0..transaction.transaction_body.outputs.len() {
182 let input = Input {
183 tx_hash,
184 index: index as u32,
185 };
186
187 debug!(
188 target: LOG_TARGET,
189 "Checking for pre-existing output {:?}", input
190 );
191
192 ensure!(
193 TransparentUtxoSet::peek_utxo(&input).is_none(),
194 UTxOError::Babbage(OutputAlreadyInUTxO)
195 );
196 }
197
198 let pallas_tx: PallasTransaction = <_>::from(transaction.clone());
201 let cbor_bytes: Vec<u8> = babbage_tx_to_cbor(&pallas_tx);
202 let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes);
203 let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone();
204 let outs_info_clone = tx_outs_info.clone();
205 let utxos: UTxOs = mk_utxo_for_babbage_tx(tx_body, outs_info_clone.as_slice());
206
207 Self::pool_checks(&mtx, &utxos)?;
208
209 let provides = (0..transaction.transaction_body.outputs.len())
212 .map(|i| {
213 let input = Input {
214 tx_hash,
215 index: i as u32,
216 };
217 input.encode()
218 })
219 .collect::<Vec<_>>();
220
221 if !missing_inputs.is_empty() {
223 debug!(
224 target: LOG_TARGET,
225 "Transaction is valid but still has missing inputs. Returning early.",
226 );
227 return Ok(ValidTransaction {
228 requires: missing_inputs,
229 provides,
230 priority: 0,
231 longevity: TransactionLongevity::MAX,
232 propagate: true,
233 });
234 }
235
236 Self::ledger_checks(&mtx, &utxos)?;
240 Self::phase_two_checks(&cbor_bytes, input_utxos)?;
241
242 Ok(ValidTransaction {
244 requires: Vec::new(),
245 provides,
246 priority: 0,
247 longevity: TransactionLongevity::MAX,
248 propagate: true,
249 })
250 }
251
252 fn apply_griffin_transaction(transaction: &Transaction) -> DispatchResult {
256 debug!(
257 target: LOG_TARGET,
258 "applying griffin transaction {:?}", transaction
259 );
260
261 let valid_transaction = Self::validate_griffin_transaction(transaction)?;
264
265 ensure!(
268 valid_transaction.requires.is_empty(),
269 UTxOError::Babbage(InputNotInUTxO)
270 );
271
272 Self::update_storage(transaction);
274
275 Ok(())
276 }
277
278 fn update_storage(transaction: &Transaction) {
283 for input in &transaction.transaction_body.inputs {
285 TransparentUtxoSet::consume_utxo(input);
286 }
287
288 debug!(
289 target: LOG_TARGET,
290 "Transaction before updating storage {:?}", transaction
291 );
292 for (index, output) in transaction.transaction_body.outputs.iter().enumerate() {
294 let input = Input {
295 tx_hash: BlakeTwo256::hash_of(&transaction.encode()),
296 index: index as u32,
297 };
298 TransparentUtxoSet::store_utxo(input, output);
299 }
300 }
301
302 pub fn block_height() -> BlockNumber {
304 sp_io::storage::get(HEIGHT_KEY)
305 .and_then(|d| BlockNumber::decode(&mut &*d).ok())
306 .expect("A height is stored at the beginning of block one and never cleared.")
307 }
308
309 pub fn zero_time() -> u64 {
312 sp_io::storage::get(ZERO_TIME)
313 .and_then(|d| u64::decode(&mut &*d).ok())
314 .expect("Failed to read ZERO_TIME from storage.")
315 }
316
317 pub fn zero_slot() -> u64 {
319 sp_io::storage::get(ZERO_SLOT)
320 .and_then(|d| u64::decode(&mut &*d).ok())
321 .expect("Failed to read ZERO_SLOT from storage.")
322 }
323
324 pub fn open_block(header: &Header) -> ExtrinsicInclusionMode {
328 debug!(
329 target: LOG_TARGET,
330 "Entering initialize_block. header: {:?}", header
331 );
332
333 sp_io::storage::set(HEADER_KEY, &header.encode());
336
337 sp_io::storage::set(HEIGHT_KEY, &header.number().encode());
340
341 ExtrinsicInclusionMode::AllExtrinsics
343 }
344
345 pub fn apply_extrinsic(extrinsic: Transaction) -> ApplyExtrinsicResult {
346 debug!(
347 target: LOG_TARGET,
348 "Entering apply_extrinsic: {:?}", extrinsic
349 );
350
351 let mut extrinsics = sp_io::storage::get(EXTRINSIC_KEY)
354 .and_then(|d| <Vec<Vec<u8>>>::decode(&mut &*d).ok())
355 .unwrap_or_default();
356 extrinsics.push(extrinsic.encode());
357 sp_io::storage::set(EXTRINSIC_KEY, &extrinsics.encode());
358
359 Self::apply_griffin_transaction(&extrinsic).map_err(|e| {
361 log::warn!(
362 target: LOG_TARGET,
363 "⛔ Griffin Transaction did not validate to be applied due to: {:?}",
364 e,
365 );
366 TransactionValidityError::Invalid(e.into())
367 })?;
368
369 Ok(Ok(()))
370 }
371
372 pub fn close_block() -> Header {
373 let mut header = sp_io::storage::get(HEADER_KEY)
374 .and_then(|d| Header::decode(&mut &*d).ok())
375 .expect("We initialized with header, it never got mutated, qed");
376
377 sp_io::storage::clear(HEADER_KEY);
380
381 let extrinsics = sp_io::storage::get(EXTRINSIC_KEY)
382 .and_then(|d| <Vec<Vec<u8>>>::decode(&mut &*d).ok())
383 .unwrap_or_default();
384 let extrinsics_root =
385 <Header as HeaderT>::Hashing::ordered_trie_root(extrinsics, StateVersion::V0);
386 sp_io::storage::clear(EXTRINSIC_KEY);
387 header.set_extrinsics_root(extrinsics_root);
388
389 let raw_state_root = &sp_io::storage::root(StateVersion::V1)[..];
390 let state_root = <Header as HeaderT>::Hash::decode(&mut &raw_state_root[..]).unwrap();
391 header.set_state_root(state_root);
392
393 debug!(target: LOG_TARGET, "finalizing block {:?}", header);
394 header
395 }
396
397 pub fn execute_block(block: Block) {
400 debug!(
401 target: LOG_TARGET,
402 "Entering execute_block. block: {:?}", block
403 );
404
405 sp_io::storage::set(HEADER_KEY, &block.header().encode());
409
410 sp_io::storage::set(HEIGHT_KEY, &block.header().number().encode());
413
414 for extrinsic in block.extrinsics() {
416 match Self::apply_griffin_transaction(&extrinsic) {
417 Ok(()) => debug!(
418 target: LOG_TARGET,
419 "Successfully executed extrinsic: {:?}", extrinsic
420 ),
421 Err(e) => panic!("{:?}", e),
422 }
423 }
424
425 sp_io::storage::clear(HEADER_KEY);
427
428 let raw_state_root = &sp_io::storage::root(StateVersion::V1)[..];
430 let state_root = <Header as HeaderT>::Hash::decode(&mut &raw_state_root[..]).unwrap();
431 assert_eq!(
432 *block.header().state_root(),
433 state_root,
434 "state root mismatch"
435 );
436
437 let extrinsics = block
439 .extrinsics()
440 .iter()
441 .map(|x| x.encode())
442 .collect::<Vec<_>>();
443 let extrinsics_root =
444 <Header as HeaderT>::Hashing::ordered_trie_root(extrinsics, StateVersion::V0);
445 assert_eq!(
446 *block.header().extrinsics_root(),
447 extrinsics_root,
448 "extrinsics root mismatch"
449 );
450 }
451
452 pub fn validate_transaction(
455 source: TransactionSource,
456 tx: Transaction,
457 block_hash: <Block as BlockT>::Hash,
458 ) -> TransactionValidity {
459 debug!(
460 target: LOG_TARGET,
461 "Entering validate_transaction. source: {:?}, tx: {:?}, block hash: {:?}",
462 source,
463 tx,
464 block_hash
465 );
466
467 let r = Self::validate_griffin_transaction(&tx).map_err(|e| {
468 log::warn!(
469 target: LOG_TARGET,
470 "⛔ Griffin Transaction did not validate (in the pool): {:?}",
471 e,
472 );
473 TransactionValidityError::Invalid(e.into())
474 });
475
476 debug!(target: LOG_TARGET, "Validation result: {:?}", r);
477
478 r
479 }
480}