1use crate::pallas_applying::{
9 babbage::{
10 check_ins_not_empty,
11 check_preservation_of_value,
13 check_witness_set,
14 },
15 utils::BabbageError::*,
16 UTxOs,
17};
18use crate::pallas_codec::utils::CborWrap;
19use crate::pallas_primitives::{
20 babbage::{
21 MintedDatumOption, MintedScriptRef, MintedTransactionBody, MintedTx,
22 Tx as PallasTransaction, Value as PallasValue,
23 },
24 conway::{MintedTx as ConwayMintedTx, TransactionOutput},
25};
26use crate::uplc::tx::{eval_phase_two, ResolvedInput, SlotConfig};
27use crate::{
28 checks_interface::{
29 babbage_minted_tx_from_cbor, babbage_tx_to_cbor, check_min_coin,
30 conway_minted_tx_from_cbor, mk_utxo_for_babbage_tx,
31 },
32 ensure,
33 types::{Block, BlockNumber, DispatchResult, Header, Input, Output, Transaction, UTxOError},
34 utxo_set::TransparentUtxoSet,
35 EXTRINSIC_KEY, HEADER_KEY, HEIGHT_KEY, LOG_TARGET,
36};
37use alloc::{collections::btree_set::BTreeSet, string::String, vec::Vec};
38use log::debug;
39use parity_scale_codec::{Decode, Encode};
40use sp_runtime::{
41 traits::{BlakeTwo256, Block as BlockT, Extrinsic, Hash as HashT, Header as HeaderT},
42 transaction_validity::{
43 TransactionLongevity, TransactionSource, TransactionValidity, TransactionValidityError,
44 ValidTransaction,
45 },
46 ApplyExtrinsicResult, ExtrinsicInclusionMode, StateVersion,
47};
48
49type OutputInfoList<'a> = Vec<(
50 String, PallasValue,
52 Option<MintedDatumOption<'a>>,
53 Option<CborWrap<MintedScriptRef<'a>>>,
54)>;
55
56pub struct Executive;
60
61impl Executive
62where
63 Block: BlockT,
64 Transaction: Extrinsic,
65{
66 fn pool_checks(mtx: &MintedTx, _utxos: &UTxOs) -> DispatchResult {
69 check_ins_not_empty(&mtx.transaction_body.clone())?;
70 Ok(())
71 }
72
73 fn ledger_checks(mtx: &MintedTx, utxos: &UTxOs) -> DispatchResult {
76 let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone();
77 check_preservation_of_value(tx_body, utxos)?;
80 check_witness_set(mtx, utxos)?;
81 check_min_coin(tx_body)?;
82
83 Ok(())
84 }
85
86 fn phase_two_checks(tx_cbor_bytes: &Vec<u8>, input_utxos: Vec<Output>) -> DispatchResult {
87 let conway_mtx: ConwayMintedTx = conway_minted_tx_from_cbor(&tx_cbor_bytes);
88 let pallas_input_utxos = input_utxos
89 .iter()
90 .map(|ri| TransactionOutput::from(ri.clone()))
91 .collect::<Vec<_>>();
92 let pallas_resolved_inputs: Vec<ResolvedInput> = conway_mtx
93 .transaction_body
94 .inputs
95 .iter()
96 .zip(pallas_input_utxos.iter())
97 .map(|(input, output)| ResolvedInput {
98 input: input.clone(),
99 output: output.clone(),
100 })
101 .collect();
102
103 let phase_two_result = eval_phase_two(
104 &conway_mtx,
105 &pallas_resolved_inputs,
106 None,
107 None,
108 &SlotConfig::default(),
109 false,
110 |_| (),
111 );
112 ensure!(
113 phase_two_result.is_ok(),
114 UTxOError::Babbage(PhaseTwoValidationError)
115 );
116
117 Ok(())
118 }
119
120 fn validate_griffin_transaction(
128 transaction: &Transaction,
129 ) -> Result<ValidTransaction, UTxOError> {
130 debug!(
131 target: LOG_TARGET,
132 "validating griffin transaction",
133 );
134
135 {
137 let input_set: BTreeSet<_> = transaction
138 .transaction_body
139 .inputs
140 .iter()
141 .map(|o| o.encode())
142 .collect();
143 ensure!(
144 input_set.len() == transaction.transaction_body.inputs.len(),
145 UTxOError::Babbage(DuplicateInput)
146 );
147 }
148
149 let mut tx_outs_info: OutputInfoList = Vec::new();
150 let mut input_utxos: Vec<Output> = Vec::new();
151
152 let mut missing_inputs = Vec::new();
155 for input in transaction.transaction_body.inputs.iter() {
156 if let Some(u) = TransparentUtxoSet::peek_utxo(&input) {
157 tx_outs_info.push((
158 hex::encode(u.address.0.as_slice()),
159 PallasValue::from(u.clone().value),
160 None, None,
162 ));
163 input_utxos.push(u);
165 } else {
166 missing_inputs.push(input.clone().encode());
167 }
168 }
169
170 let tx_hash = BlakeTwo256::hash_of(&transaction.encode());
172 for index in 0..transaction.transaction_body.outputs.len() {
173 let input = Input {
174 tx_hash,
175 index: index as u32,
176 };
177
178 debug!(
179 target: LOG_TARGET,
180 "Checking for pre-existing output {:?}", input
181 );
182
183 ensure!(
184 TransparentUtxoSet::peek_utxo(&input).is_none(),
185 UTxOError::Babbage(OutputAlreadyInUTxO)
186 );
187 }
188
189 let pallas_tx: PallasTransaction = <_>::from(transaction.clone());
192 let cbor_bytes: Vec<u8> = babbage_tx_to_cbor(&pallas_tx);
193 let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes);
194 let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone();
195 let outs_info_clone = tx_outs_info.clone();
196 let utxos: UTxOs = mk_utxo_for_babbage_tx(tx_body, outs_info_clone.as_slice());
197
198 Self::pool_checks(&mtx, &utxos)?;
199
200 let provides = (0..transaction.transaction_body.outputs.len())
203 .map(|i| {
204 let input = Input {
205 tx_hash,
206 index: i as u32,
207 };
208 input.encode()
209 })
210 .collect::<Vec<_>>();
211
212 if !missing_inputs.is_empty() {
214 debug!(
215 target: LOG_TARGET,
216 "Transaction is valid but still has missing inputs. Returning early.",
217 );
218 return Ok(ValidTransaction {
219 requires: missing_inputs,
220 provides,
221 priority: 0,
222 longevity: TransactionLongevity::MAX,
223 propagate: true,
224 });
225 }
226
227 Self::ledger_checks(&mtx, &utxos)?;
231 Self::phase_two_checks(&cbor_bytes, input_utxos)?;
232
233 Ok(ValidTransaction {
235 requires: Vec::new(),
236 provides,
237 priority: 0,
238 longevity: TransactionLongevity::MAX,
239 propagate: true,
240 })
241 }
242
243 fn apply_griffin_transaction(transaction: &Transaction) -> DispatchResult {
247 debug!(
248 target: LOG_TARGET,
249 "applying griffin transaction {:?}", transaction
250 );
251
252 let valid_transaction = Self::validate_griffin_transaction(transaction)?;
255
256 ensure!(
259 valid_transaction.requires.is_empty(),
260 UTxOError::Babbage(InputNotInUTxO)
261 );
262
263 Self::update_storage(transaction);
265
266 Ok(())
267 }
268
269 fn update_storage(transaction: &Transaction) {
274 for input in &transaction.transaction_body.inputs {
276 TransparentUtxoSet::consume_utxo(input);
277 }
278
279 debug!(
280 target: LOG_TARGET,
281 "Transaction before updating storage {:?}", transaction
282 );
283 for (index, output) in transaction.transaction_body.outputs.iter().enumerate() {
285 let input = Input {
286 tx_hash: BlakeTwo256::hash_of(&transaction.encode()),
287 index: index as u32,
288 };
289 TransparentUtxoSet::store_utxo(input, output);
290 }
291 }
292
293 pub fn block_height() -> BlockNumber {
295 sp_io::storage::get(HEIGHT_KEY)
296 .and_then(|d| BlockNumber::decode(&mut &*d).ok())
297 .expect("A height is stored at the beginning of block one and never cleared.")
298 }
299
300 pub fn open_block(header: &Header) -> ExtrinsicInclusionMode {
304 debug!(
305 target: LOG_TARGET,
306 "Entering initialize_block. header: {:?}", header
307 );
308
309 sp_io::storage::set(HEADER_KEY, &header.encode());
312
313 sp_io::storage::set(HEIGHT_KEY, &header.number().encode());
316
317 ExtrinsicInclusionMode::AllExtrinsics
319 }
320
321 pub fn apply_extrinsic(extrinsic: Transaction) -> ApplyExtrinsicResult {
322 debug!(
323 target: LOG_TARGET,
324 "Entering apply_extrinsic: {:?}", extrinsic
325 );
326
327 let mut extrinsics = sp_io::storage::get(EXTRINSIC_KEY)
330 .and_then(|d| <Vec<Vec<u8>>>::decode(&mut &*d).ok())
331 .unwrap_or_default();
332 extrinsics.push(extrinsic.encode());
333 sp_io::storage::set(EXTRINSIC_KEY, &extrinsics.encode());
334
335 Self::apply_griffin_transaction(&extrinsic).map_err(|e| {
337 log::warn!(
338 target: LOG_TARGET,
339 "⛔ Griffin Transaction did not validate to be applied due to: {:?}",
340 e,
341 );
342 TransactionValidityError::Invalid(e.into())
343 })?;
344
345 Ok(Ok(()))
346 }
347
348 pub fn close_block() -> Header {
349 let mut header = sp_io::storage::get(HEADER_KEY)
350 .and_then(|d| Header::decode(&mut &*d).ok())
351 .expect("We initialized with header, it never got mutated, qed");
352
353 sp_io::storage::clear(HEADER_KEY);
356
357 let extrinsics = sp_io::storage::get(EXTRINSIC_KEY)
358 .and_then(|d| <Vec<Vec<u8>>>::decode(&mut &*d).ok())
359 .unwrap_or_default();
360 let extrinsics_root =
361 <Header as HeaderT>::Hashing::ordered_trie_root(extrinsics, StateVersion::V0);
362 sp_io::storage::clear(EXTRINSIC_KEY);
363 header.set_extrinsics_root(extrinsics_root);
364
365 let raw_state_root = &sp_io::storage::root(StateVersion::V1)[..];
366 let state_root = <Header as HeaderT>::Hash::decode(&mut &raw_state_root[..]).unwrap();
367 header.set_state_root(state_root);
368
369 debug!(target: LOG_TARGET, "finalizing block {:?}", header);
370 header
371 }
372
373 pub fn execute_block(block: Block) {
376 debug!(
377 target: LOG_TARGET,
378 "Entering execute_block. block: {:?}", block
379 );
380
381 sp_io::storage::set(HEADER_KEY, &block.header().encode());
385
386 sp_io::storage::set(HEIGHT_KEY, &block.header().number().encode());
389
390 for extrinsic in block.extrinsics() {
392 match Self::apply_griffin_transaction(&extrinsic) {
393 Ok(()) => debug!(
394 target: LOG_TARGET,
395 "Successfully executed extrinsic: {:?}", extrinsic
396 ),
397 Err(e) => panic!("{:?}", e),
398 }
399 }
400
401 sp_io::storage::clear(HEADER_KEY);
403
404 let raw_state_root = &sp_io::storage::root(StateVersion::V1)[..];
406 let state_root = <Header as HeaderT>::Hash::decode(&mut &raw_state_root[..]).unwrap();
407 assert_eq!(
408 *block.header().state_root(),
409 state_root,
410 "state root mismatch"
411 );
412
413 let extrinsics = block
415 .extrinsics()
416 .iter()
417 .map(|x| x.encode())
418 .collect::<Vec<_>>();
419 let extrinsics_root =
420 <Header as HeaderT>::Hashing::ordered_trie_root(extrinsics, StateVersion::V0);
421 assert_eq!(
422 *block.header().extrinsics_root(),
423 extrinsics_root,
424 "extrinsics root mismatch"
425 );
426 }
427
428 pub fn validate_transaction(
431 source: TransactionSource,
432 tx: Transaction,
433 block_hash: <Block as BlockT>::Hash,
434 ) -> TransactionValidity {
435 debug!(
436 target: LOG_TARGET,
437 "Entering validate_transaction. source: {:?}, tx: {:?}, block hash: {:?}",
438 source,
439 tx,
440 block_hash
441 );
442
443 let r = Self::validate_griffin_transaction(&tx).map_err(|e| {
444 log::warn!(
445 target: LOG_TARGET,
446 "⛔ Griffin Transaction did not validate (in the pool): {:?}",
447 e,
448 );
449 TransactionValidityError::Invalid(e.into())
450 });
451
452 debug!(target: LOG_TARGET, "Validation result: {:?}", r);
453
454 r
455 }
456}