1pub(crate) mod wasm_v2_request;
2
3use casper_executor_wasm::ExecutorV2;
4use itertools::Itertools;
5use std::{collections::BTreeMap, convert::TryInto, sync::Arc, time::Instant};
6use tracing::{debug, error, info, trace, warn};
7use wasm_v2_request::{WasmV2Request, WasmV2Result};
8
9use casper_execution_engine::engine_state::{
10 BlockInfo, ExecutionEngineV1, WasmV1Request, WasmV1Result,
11};
12use casper_storage::{
13 block_store::types::ApprovalsHashes,
14 data_access_layer::{
15 balance::BalanceHandling,
16 mint::{BalanceIdentifierTransferArgs, BurnRequest},
17 AuctionMethod, BalanceHoldKind, BalanceHoldRequest, BalanceIdentifier,
18 BalanceIdentifierPurseRequest, BalanceIdentifierPurseResult, BalanceRequest,
19 BiddingRequest, BlockGlobalRequest, BlockGlobalResult, BlockRewardsRequest,
20 BlockRewardsResult, DataAccessLayer, EntryPointRequest, EntryPointResult,
21 EraValidatorsRequest, EraValidatorsResult, EvictItem, FeeRequest, FeeResult, FlushRequest,
22 HandleFeeMode, HandleFeeRequest, HandleRefundMode, HandleRefundRequest,
23 InsufficientBalanceHandling, ProofHandling, PruneRequest, PruneResult, StepRequest,
24 StepResult, TransferRequest,
25 },
26 global_state::state::{
27 lmdb::LmdbGlobalState, scratch::ScratchGlobalState, CommitProvider, ScratchProvider,
28 StateProvider, StateReader,
29 },
30 system::runtime_native::Config as NativeRuntimeConfig,
31};
32use casper_types::{
33 bytesrepr::{self, ToBytes, U32_SERIALIZED_LENGTH},
34 execution::{Effects, ExecutionResult, TransformKindV2, TransformV2},
35 system::handle_payment::ARG_AMOUNT,
36 BlockHash, BlockHeader, BlockTime, BlockV2, CLValue, Chainspec, ChecksumRegistry, Digest,
37 EntityAddr, EraEndV2, EraId, FeeHandling, Gas, InvalidTransaction, InvalidTransactionV1, Key,
38 ProtocolVersion, PublicKey, RefundHandling, Transaction, TransactionEntryPoint,
39 AUCTION_LANE_ID, MINT_LANE_ID, U512,
40};
41
42use super::{
43 types::{SpeculativeExecutionResult, StepOutcome},
44 utils::{self, calculate_prune_eras},
45 BlockAndExecutionArtifacts, BlockExecutionError, ExecutionPreState, Metrics, StateResultError,
46 APPROVALS_CHECKSUM_NAME, EXECUTION_RESULTS_CHECKSUM_NAME,
47};
48use crate::{
49 components::fetcher::FetchItem,
50 contract_runtime::types::ExecutionArtifactBuilder,
51 types::{self, Chunkable, ExecutableBlock, InternalEraReport, MetaTransaction},
52};
53
54#[allow(clippy::too_many_arguments)]
56pub fn execute_finalized_block(
57 data_access_layer: &DataAccessLayer<LmdbGlobalState>,
58 execution_engine_v1: &ExecutionEngineV1,
59 execution_engine_v2: ExecutorV2,
60 chainspec: &Chainspec,
61 metrics: Option<Arc<Metrics>>,
62 execution_pre_state: ExecutionPreState,
63 executable_block: ExecutableBlock,
64 key_block_height_for_activation_point: u64,
65 current_gas_price: u8,
66 next_era_gas_price: Option<u8>,
67 last_switch_block_hash: Option<BlockHash>,
68) -> Result<BlockAndExecutionArtifacts, BlockExecutionError> {
69 let block_height = executable_block.height;
70 if block_height != execution_pre_state.next_block_height() {
71 return Err(BlockExecutionError::WrongBlockHeight {
72 executable_block: Box::new(executable_block),
73 execution_pre_state: Box::new(execution_pre_state),
74 });
75 }
76 if executable_block.era_report.is_some() && next_era_gas_price.is_none() {
77 return Err(BlockExecutionError::FailedToGetNewEraGasPrice {
78 era_id: executable_block.era_id.successor(),
79 });
80 }
81 let start = Instant::now();
82 let protocol_version = chainspec.protocol_version();
83 let activation_point_era_id = chainspec.protocol_config.activation_point.era_id();
84 let prune_batch_size = chainspec.core_config.prune_batch_size;
85 let native_runtime_config = NativeRuntimeConfig::from_chainspec(chainspec);
86 let addressable_entity_enabled = chainspec.core_config.enable_addressable_entity();
87
88 if addressable_entity_enabled != data_access_layer.enable_addressable_entity {
89 return Err(BlockExecutionError::InvalidAESetting(
90 data_access_layer.enable_addressable_entity,
91 ));
92 }
93
94 let parent_hash = execution_pre_state.parent_hash();
96 let parent_seed = execution_pre_state.parent_seed();
97 let parent_block_hash = execution_pre_state.parent_hash();
98 let pre_state_root_hash = execution_pre_state.pre_state_root_hash();
99 let mut state_root_hash = pre_state_root_hash; let payment_balance_addr =
102 match data_access_layer.balance_purse(BalanceIdentifierPurseRequest::new(
103 state_root_hash,
104 protocol_version,
105 BalanceIdentifier::Payment,
106 )) {
107 BalanceIdentifierPurseResult::RootNotFound => {
108 return Err(BlockExecutionError::RootNotFound(state_root_hash))
109 }
110 BalanceIdentifierPurseResult::Failure(tce) => {
111 return Err(BlockExecutionError::BlockGlobal(format!("{:?}", tce)));
112 }
113 BalanceIdentifierPurseResult::Success { purse_addr } => purse_addr,
114 };
115
116 let block_time = BlockTime::new(executable_block.timestamp.millis());
118
119 let proposer = executable_block.proposer.clone();
120 let era_id = executable_block.era_id;
121 let mut artifacts = Vec::with_capacity(executable_block.transactions.len());
122
123 let insufficient_balance_handling = InsufficientBalanceHandling::HoldRemaining;
125 let refund_handling = chainspec.core_config.refund_handling;
126 let fee_handling = chainspec.core_config.fee_handling;
127 let baseline_motes_amount = chainspec.core_config.baseline_motes_amount_u512();
128 let balance_handling = BalanceHandling::Available;
129
130 let scratch_state = data_access_layer.get_scratch_global_state();
133
134 if let Some(metrics) = metrics.as_ref() {
136 metrics
137 .exec_block_pre_processing
138 .observe(start.elapsed().as_secs_f64());
139 }
140
141 let transaction_ids = executable_block
143 .transactions
144 .iter()
145 .map(Transaction::fetch_id)
146 .collect_vec();
147
148 let txn_processing_start = Instant::now();
150
151 match scratch_state.block_global(BlockGlobalRequest::block_time(
155 state_root_hash,
156 protocol_version,
157 block_time,
158 )) {
159 BlockGlobalResult::RootNotFound => {
160 return Err(BlockExecutionError::RootNotFound(state_root_hash));
161 }
162 BlockGlobalResult::Failure(err) => {
163 return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err)));
164 }
165 BlockGlobalResult::Success {
166 post_state_hash, ..
167 } => {
168 state_root_hash = post_state_hash;
169 }
170 }
171
172 match scratch_state.block_global(BlockGlobalRequest::set_protocol_version(
174 state_root_hash,
175 protocol_version,
176 )) {
177 BlockGlobalResult::RootNotFound => {
178 return Err(BlockExecutionError::RootNotFound(state_root_hash));
179 }
180 BlockGlobalResult::Failure(err) => {
181 return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err)));
182 }
183 BlockGlobalResult::Success {
184 post_state_hash, ..
185 } => {
186 state_root_hash = post_state_hash;
187 }
188 }
189
190 match scratch_state.block_global(BlockGlobalRequest::set_addressable_entity(
192 state_root_hash,
193 protocol_version,
194 addressable_entity_enabled,
195 )) {
196 BlockGlobalResult::RootNotFound => {
197 return Err(BlockExecutionError::RootNotFound(state_root_hash));
198 }
199 BlockGlobalResult::Failure(err) => {
200 return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err)));
201 }
202 BlockGlobalResult::Success {
203 post_state_hash, ..
204 } => {
205 state_root_hash = post_state_hash;
206 }
207 }
208
209 let transaction_config = &chainspec.transaction_config;
210
211 for stored_transaction in executable_block.transactions {
212 let mut artifact_builder = ExecutionArtifactBuilder::new(
213 &stored_transaction,
214 baseline_motes_amount, current_gas_price,
216 );
217 let transaction = MetaTransaction::from_transaction(
218 &stored_transaction,
219 chainspec.core_config.pricing_handling,
220 transaction_config,
221 )
222 .map_err(|err| BlockExecutionError::TransactionConversion(err.to_string()))?;
223 let initiator_addr = transaction.initiator_addr();
224 let transaction_hash = transaction.hash();
225 let transaction_args = transaction.session_args().clone();
226 let entry_point = transaction.entry_point();
227 let authorization_keys = transaction.signers();
228
229 let gas_limit =
253 match stored_transaction.gas_limit(chainspec, transaction.transaction_lane()) {
254 Ok(gas) => gas,
255 Err(ite) => {
256 debug!(%transaction_hash, %ite, "invalid transaction (gas limit)");
257 artifact_builder.with_invalid_transaction(&ite);
258 artifacts.push(artifact_builder.build());
259 continue;
260 }
261 };
262 artifact_builder.with_gas_limit(gas_limit);
263
264 let cost = match stored_transaction.gas_cost(
266 chainspec,
267 transaction.transaction_lane(),
268 current_gas_price,
269 ) {
270 Ok(motes) => motes.value(),
271 Err(ite) => {
272 debug!(%transaction_hash, "invalid transaction (motes conversion)");
273 artifact_builder.with_invalid_transaction(&ite);
274 artifacts.push(artifact_builder.build());
275 continue;
276 }
277 };
278 artifact_builder.with_added_cost(cost);
279
280 let is_standard_payment = transaction.is_standard_payment();
281 let is_custom_payment = !is_standard_payment && transaction.is_custom_payment();
282 let is_v1_wasm = transaction.is_v1_wasm();
283 let is_v2_wasm = transaction.is_v2_wasm();
284 let refund_purse_active = is_custom_payment;
285 if refund_purse_active {
286 let handle_refund_request = HandleRefundRequest::new(
291 native_runtime_config.clone(),
292 state_root_hash,
293 protocol_version,
294 transaction_hash,
295 HandleRefundMode::SetRefundPurse {
296 target: Box::new(initiator_addr.clone().into()),
297 },
298 );
299 let handle_refund_result = scratch_state.handle_refund(handle_refund_request);
300 if let Err(root_not_found) =
301 artifact_builder.with_set_refund_purse_result(&handle_refund_result)
302 {
303 if root_not_found {
304 return Err(BlockExecutionError::RootNotFound(state_root_hash));
305 }
306 artifacts.push(artifact_builder.build());
307 continue; }
309 state_root_hash = scratch_state
310 .commit_effects(state_root_hash, handle_refund_result.effects().clone())?;
311 }
312
313 {
314 let initial_balance_result = scratch_state.balance(BalanceRequest::new(
316 state_root_hash,
317 protocol_version,
318 initiator_addr.clone().into(),
319 balance_handling,
320 ProofHandling::NoProofs,
321 ));
322
323 if let Err(root_not_found) = artifact_builder
324 .with_initial_balance_result(initial_balance_result.clone(), baseline_motes_amount)
325 {
326 if root_not_found {
327 return Err(BlockExecutionError::RootNotFound(state_root_hash));
328 }
329 trace!(%transaction_hash, "insufficient initial balance");
330 debug!(%transaction_hash, ?initial_balance_result, %baseline_motes_amount, "insufficient initial balance");
331 artifacts.push(artifact_builder.build());
332 continue;
335 }
336 }
337
338 let mut balance_identifier = {
339 if is_standard_payment {
340 let contract_might_pay =
341 addressable_entity_enabled && transaction.is_contract_by_hash_invocation();
342
343 if contract_might_pay {
344 match invoked_contract_will_pay(&scratch_state, state_root_hash, &transaction) {
345 Ok(Some(entity_addr)) => BalanceIdentifier::Entity(entity_addr),
346 Ok(None) => {
347 trace!(%transaction_hash, "direct invocation with account payment");
349 initiator_addr.clone().into()
350 }
351 Err(err) => {
352 trace!(%transaction_hash, "failed to resolve contract self payment");
353 artifact_builder
354 .with_state_result_error(err)
355 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
356 BalanceIdentifier::PenalizedAccount(
357 initiator_addr.clone().account_hash(),
358 )
359 }
360 }
361 } else {
362 trace!(%transaction_hash, "account session with standard payment");
364 initiator_addr.clone().into()
365 }
366 } else if is_v2_wasm {
367 initiator_addr.clone().into()
371 } else if is_custom_payment {
372 let custom_payment_gas_limit =
376 Gas::new(chainspec.transaction_config.native_transfer_minimum_motes * 5);
377 let pay_result = match WasmV1Request::new_custom_payment(
378 BlockInfo::new(
379 state_root_hash,
380 block_time,
381 parent_block_hash,
382 block_height,
383 protocol_version,
384 ),
385 custom_payment_gas_limit,
386 &transaction.to_payment_input_data(),
387 ) {
388 Ok(mut pay_request) => {
389 pay_request
390 .args
391 .insert(ARG_AMOUNT, cost)
392 .map_err(|e| BlockExecutionError::PaymentError(e.to_string()))?;
393 execution_engine_v1.execute(&scratch_state, pay_request)
394 }
395 Err(error) => {
396 WasmV1Result::invalid_executable_item(custom_payment_gas_limit, error)
397 }
398 };
399
400 let insufficient_payment_deposited =
401 !pay_result.balance_increased_by_amount(payment_balance_addr, cost);
402
403 if insufficient_payment_deposited || pay_result.error().is_some() {
404 let transfer_result = scratch_state.transfer(TransferRequest::new_indirect(
408 native_runtime_config.clone(),
409 state_root_hash,
410 protocol_version,
411 transaction_hash,
412 initiator_addr.clone(),
413 authorization_keys.clone(),
414 BalanceIdentifierTransferArgs::new(
415 None,
416 initiator_addr.clone().into(),
417 BalanceIdentifier::Payment,
418 baseline_motes_amount,
419 None,
420 ),
421 ));
422
423 let msg = match pay_result.error() {
424 Some(err) => format!("{}", err),
425 None => {
426 if insufficient_payment_deposited {
427 "Insufficient custom payment".to_string()
428 } else {
429 let unk = "Unknown custom payment issue";
431 warn!(%transaction_hash, unk);
432 debug_assert!(false, "{}", unk);
433 unk.to_string()
434 }
435 }
436 };
437 state_root_hash = scratch_state
439 .commit_effects(state_root_hash, transfer_result.effects().clone())?;
440 artifact_builder
441 .with_error_message(msg)
442 .with_transfer_result(transfer_result)
443 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
444 trace!(%transaction_hash, balance_identifier=?BalanceIdentifier::PenalizedPayment, "account session with custom payment failed");
445 BalanceIdentifier::PenalizedPayment
446 } else {
447 state_root_hash = scratch_state
449 .commit_effects(state_root_hash, pay_result.effects().clone())?;
450 artifact_builder
451 .with_wasm_v1_result(pay_result)
452 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
453 trace!(%transaction_hash, balance_identifier=?BalanceIdentifier::Payment, "account session with custom payment success");
454 BalanceIdentifier::Payment
455 }
456 } else {
457 BalanceIdentifier::PenalizedAccount(initiator_addr.clone().account_hash())
458 }
459 };
460
461 let post_payment_balance_result = scratch_state.balance(BalanceRequest::new(
462 state_root_hash,
463 protocol_version,
464 balance_identifier.clone(),
465 balance_handling,
466 ProofHandling::NoProofs,
467 ));
468
469 let lane_id = transaction.transaction_lane();
470
471 let allow_execution = {
472 let is_not_penalized = !balance_identifier.is_penalty();
473 let is_sufficient_balance =
479 is_custom_payment || post_payment_balance_result.is_sufficient(cost);
480 let is_allowed_by_chainspec = chainspec.is_supported(lane_id);
481 let allow = is_not_penalized && is_sufficient_balance && is_allowed_by_chainspec;
482 if !allow {
483 if artifact_builder.error_message().is_none() {
484 artifact_builder.with_error_message(format!(
485 "penalized: {}, sufficient balance: {}, allowed by chainspec: {}",
486 !is_not_penalized, is_sufficient_balance, is_allowed_by_chainspec
487 ));
488 }
489 info!(%transaction_hash, ?balance_identifier, ?is_sufficient_balance, ?is_not_penalized, ?is_allowed_by_chainspec, "payment preprocessing unsuccessful");
490 } else {
491 debug!(%transaction_hash, ?balance_identifier, ?is_sufficient_balance, ?is_not_penalized, ?is_allowed_by_chainspec, "payment preprocessing successful");
492 }
493 allow
494 };
495
496 if allow_execution {
497 debug!(%transaction_hash, ?allow_execution, "execution allowed");
498 if is_standard_payment {
499 let hold_amount = cost;
501 let hold_request = BalanceHoldRequest::new_processing_hold(
502 state_root_hash,
503 protocol_version,
504 balance_identifier.clone(),
505 hold_amount,
506 insufficient_balance_handling,
507 );
508 let hold_result = scratch_state.balance_hold(hold_request);
509 state_root_hash =
510 scratch_state.commit_effects(state_root_hash, hold_result.effects().clone())?;
511 artifact_builder
512 .with_balance_hold_result(&hold_result)
513 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
514 }
515
516 trace!(%transaction_hash, ?lane_id, "eligible for execution");
517 match lane_id {
518 lane_id if lane_id == MINT_LANE_ID => {
519 let runtime_args = transaction_args
520 .as_named()
521 .ok_or(BlockExecutionError::InvalidTransactionArgs)?;
522 let entry_point = transaction.entry_point();
523 if let TransactionEntryPoint::Transfer = entry_point {
524 let transfer_result =
525 scratch_state.transfer(TransferRequest::with_runtime_args(
526 native_runtime_config.clone(),
527 state_root_hash,
528 protocol_version,
529 transaction_hash,
530 initiator_addr.clone(),
531 authorization_keys,
532 runtime_args.clone(),
533 ));
534 state_root_hash = scratch_state
535 .commit_effects(state_root_hash, transfer_result.effects().clone())?;
536 artifact_builder
537 .with_min_cost(gas_limit.value())
538 .with_added_consumed(gas_limit)
539 .with_transfer_result(transfer_result)
540 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
541 } else if let TransactionEntryPoint::Burn = entry_point {
542 let burn_result = scratch_state.burn(BurnRequest::with_runtime_args(
543 native_runtime_config.clone(),
544 state_root_hash,
545 protocol_version,
546 transaction_hash,
547 initiator_addr.clone(),
548 authorization_keys,
549 runtime_args.clone(),
550 ));
551 state_root_hash = scratch_state
552 .commit_effects(state_root_hash, burn_result.effects().clone())?;
553 artifact_builder
554 .with_min_cost(gas_limit.value())
555 .with_added_consumed(gas_limit)
556 .with_burn_result(burn_result)
557 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
558 } else {
559 artifact_builder.with_error_message(format!(
560 "Attempt to call unsupported native mint entrypoint: {}",
561 entry_point
562 ));
563 }
564 }
565 lane_id if lane_id == AUCTION_LANE_ID => {
566 let runtime_args = transaction_args
567 .as_named()
568 .ok_or(BlockExecutionError::InvalidTransactionArgs)?;
569 match AuctionMethod::from_parts(entry_point, runtime_args, chainspec) {
570 Ok(auction_method) => {
571 let bidding_result = scratch_state.bidding(BiddingRequest::new(
572 native_runtime_config.clone(),
573 state_root_hash,
574 protocol_version,
575 transaction_hash,
576 initiator_addr.clone(),
577 authorization_keys,
578 auction_method,
579 ));
580 state_root_hash = scratch_state.commit_effects(
581 state_root_hash,
582 bidding_result.effects().clone(),
583 )?;
584 artifact_builder
585 .with_min_cost(gas_limit.value())
586 .with_added_consumed(gas_limit)
587 .with_bidding_result(bidding_result)
588 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
589 }
590 Err(ame) => {
591 error!(
592 %transaction_hash,
593 ?ame,
594 "failed to determine auction method"
595 );
596 artifact_builder.with_auction_method_error(&ame);
597 }
598 };
599 }
600 _ if is_v1_wasm => {
601 let wasm_v1_start = Instant::now();
602 let session_input_data = transaction.to_session_input_data();
603 match WasmV1Request::new_session(
604 BlockInfo::new(
605 state_root_hash,
606 block_time,
607 parent_block_hash,
608 block_height,
609 protocol_version,
610 ),
611 gas_limit,
612 &session_input_data,
613 ) {
614 Ok(wasm_v1_request) => {
615 trace!(%transaction_hash, ?lane_id, ?wasm_v1_request, "able to get wasm v1 request");
616 let wasm_v1_result =
617 execution_engine_v1.execute(&scratch_state, wasm_v1_request);
618 trace!(%transaction_hash, ?lane_id, ?wasm_v1_result, "able to get wasm v1 result");
619 state_root_hash = scratch_state.commit_effects(
620 state_root_hash,
621 wasm_v1_result.effects().clone(),
622 )?;
623 artifact_builder
625 .with_wasm_v1_result(wasm_v1_result)
626 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
627 }
628 Err(ire) => {
629 debug!(%transaction_hash, ?lane_id, ?ire, "unable to get wasm v1 request");
630 artifact_builder.with_invalid_wasm_v1_request(&ire);
631 }
632 };
633 if let Some(metrics) = metrics.as_ref() {
634 metrics
635 .exec_wasm_v1
636 .observe(wasm_v1_start.elapsed().as_secs_f64());
637 }
638 }
639 _ if is_v2_wasm => match WasmV2Request::new(
640 gas_limit,
641 chainspec.network_config.name.clone(),
642 state_root_hash,
643 parent_block_hash,
644 block_height,
645 &transaction,
646 ) {
647 Ok(wasm_v2_request) => {
648 match wasm_v2_request.execute(
649 &execution_engine_v2,
650 state_root_hash,
651 &scratch_state,
652 ) {
653 Ok(wasm_v2_result) => {
654 match &wasm_v2_result {
655 WasmV2Result::Install(install_result) => {
656 info!(
657 contract_hash=base16::encode_lower(&install_result.smart_contract_addr()),
658 pre_state_root_hash=%state_root_hash,
659 post_state_root_hash=%install_result.post_state_hash(),
660 "install contract result");
661 }
662
663 WasmV2Result::Execute(execute_result) => {
664 info!(
665 pre_state_root_hash=%state_root_hash,
666 post_state_root_hash=%execute_result.post_state_hash(),
667 host_error=?execute_result.host_error.as_ref(),
668 "execute contract result");
669 }
670 }
671
672 state_root_hash = wasm_v2_result.post_state_hash();
673 artifact_builder.with_wasm_v2_result(wasm_v2_result);
674 }
675 Err(wasm_v2_error) => {
676 artifact_builder.with_wasm_v2_error(wasm_v2_error);
677 }
678 }
679 }
680 Err(ire) => {
681 debug!(%transaction_hash, ?lane_id, ?ire, "unable to get wasm v2 request");
682 artifact_builder.with_invalid_wasm_v2_request(ire);
683 }
684 },
685 _ => {
686 unreachable!("Unknown VM target")
689 }
690 }
691 }
692
693 {
695 let hold_request = BalanceHoldRequest::new_clear(
696 state_root_hash,
697 protocol_version,
698 BalanceHoldKind::All,
699 balance_identifier.clone(),
700 );
701 let hold_result = scratch_state.balance_hold(hold_request);
702 state_root_hash =
703 scratch_state.commit_effects(state_root_hash, hold_result.effects().clone())?;
704 artifact_builder
705 .with_balance_hold_result(&hold_result)
706 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
707 }
708
709 let refund_amount = {
711 let consumed =
712 if balance_identifier.is_penalty() || artifact_builder.error_message().is_some() {
713 artifact_builder.cost_to_use() } else {
715 artifact_builder.consumed()
716 };
717
718 let refund_mode = match refund_handling {
719 RefundHandling::NoRefund => {
720 if fee_handling.is_no_fee() && is_custom_payment {
721 balance_identifier = BalanceIdentifier::Refund;
726 Some(HandleRefundMode::RefundNoFeeCustomPayment {
727 initiator_addr: Box::new(initiator_addr.clone()),
728 limit: gas_limit.value(),
729 gas_price: current_gas_price,
730 cost,
731 })
732 } else {
733 None
734 }
735 }
736 RefundHandling::Burn { refund_ratio } => Some(HandleRefundMode::Burn {
737 limit: gas_limit.value(),
738 gas_price: current_gas_price,
739 cost,
740 consumed,
741 source: Box::new(balance_identifier.clone()),
742 ratio: refund_ratio,
743 }),
744 RefundHandling::Refund { refund_ratio } => {
745 let source = Box::new(balance_identifier.clone());
746 if is_custom_payment {
747 let target = Box::new(BalanceIdentifier::Refund);
760 Some(HandleRefundMode::Refund {
761 initiator_addr: Box::new(initiator_addr.clone()),
762 limit: gas_limit.value(),
763 gas_price: current_gas_price,
764 consumed,
765 cost,
766 ratio: refund_ratio,
767 source,
768 target,
769 })
770 } else {
771 Some(HandleRefundMode::CalculateAmount {
781 limit: gas_limit.value(),
782 gas_price: current_gas_price,
783 consumed,
784 cost,
785 ratio: refund_ratio,
786 source,
787 })
788 }
789 }
790 };
791 match refund_mode {
792 Some(refund_mode) => {
793 let handle_refund_request = HandleRefundRequest::new(
794 native_runtime_config.clone(),
795 state_root_hash,
796 protocol_version,
797 transaction_hash,
798 refund_mode,
799 );
800 let handle_refund_result = scratch_state.handle_refund(handle_refund_request);
801 let refunded_amount = handle_refund_result.refund_amount();
802 state_root_hash = scratch_state
803 .commit_effects(state_root_hash, handle_refund_result.effects().clone())?;
804 artifact_builder
805 .with_handle_refund_result(&handle_refund_result)
806 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
807
808 refunded_amount
809 }
810 None => U512::zero(),
811 }
812 };
813 artifact_builder.with_refund_amount(refund_amount);
814 let handle_fee_result = match fee_handling {
816 FeeHandling::NoFee => {
817 let amount = cost.saturating_sub(refund_amount);
819 let hold_request = BalanceHoldRequest::new_gas_hold(
820 state_root_hash,
821 protocol_version,
822 balance_identifier,
823 amount,
824 insufficient_balance_handling,
825 );
826 let hold_result = scratch_state.balance_hold(hold_request);
827 state_root_hash =
828 scratch_state.commit_effects(state_root_hash, hold_result.effects().clone())?;
829 artifact_builder
830 .with_balance_hold_result(&hold_result)
831 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
832 let handle_fee_request = HandleFeeRequest::new(
833 native_runtime_config.clone(),
834 state_root_hash,
835 protocol_version,
836 transaction_hash,
837 HandleFeeMode::credit(proposer.clone(), amount, era_id),
838 );
839 scratch_state.handle_fee(handle_fee_request)
840 }
841 FeeHandling::Burn => {
842 let amount = cost.saturating_sub(refund_amount);
844 let handle_fee_request = HandleFeeRequest::new(
845 native_runtime_config.clone(),
846 state_root_hash,
847 protocol_version,
848 transaction_hash,
849 HandleFeeMode::burn(balance_identifier, Some(amount)),
850 );
851 scratch_state.handle_fee(handle_fee_request)
852 }
853 FeeHandling::PayToProposer => {
854 let amount = cost.saturating_sub(refund_amount);
856 let handle_fee_request = HandleFeeRequest::new(
857 native_runtime_config.clone(),
858 state_root_hash,
859 protocol_version,
860 transaction_hash,
861 HandleFeeMode::pay(
862 Box::new(initiator_addr.clone()),
863 balance_identifier,
864 BalanceIdentifier::Public(*(proposer.clone())),
865 amount,
866 ),
867 );
868 scratch_state.handle_fee(handle_fee_request)
869 }
870 FeeHandling::Accumulate => {
871 let amount = cost.saturating_sub(refund_amount);
874 let handle_fee_request = HandleFeeRequest::new(
875 native_runtime_config.clone(),
876 state_root_hash,
877 protocol_version,
878 transaction_hash,
879 HandleFeeMode::pay(
880 Box::new(initiator_addr.clone()),
881 balance_identifier,
882 BalanceIdentifier::Accumulate,
883 amount,
884 ),
885 );
886 scratch_state.handle_fee(handle_fee_request)
887 }
888 };
889
890 state_root_hash =
891 scratch_state.commit_effects(state_root_hash, handle_fee_result.effects().clone())?;
892
893 artifact_builder
894 .with_handle_fee_result(&handle_fee_result)
895 .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?;
896
897 if refund_purse_active {
899 let handle_refund_request = HandleRefundRequest::new(
904 native_runtime_config.clone(),
905 state_root_hash,
906 protocol_version,
907 transaction_hash,
908 HandleRefundMode::ClearRefundPurse,
909 );
910 let handle_refund_result = scratch_state.handle_refund(handle_refund_request);
911 if let Err(root_not_found) =
912 artifact_builder.with_clear_refund_purse_result(&handle_refund_result)
913 {
914 if root_not_found {
915 return Err(BlockExecutionError::RootNotFound(state_root_hash));
916 }
917 warn!(
918 "{}",
919 artifact_builder.error_message().unwrap_or(
920 "unknown error encountered when attempting to clear refund purse"
921 .to_string()
922 )
923 );
924 }
925 state_root_hash = scratch_state
926 .commit_effects(state_root_hash, handle_refund_result.effects().clone())?;
927 }
928
929 artifacts.push(artifact_builder.build());
930 }
931
932 if let Some(metrics) = metrics.as_ref() {
934 metrics
935 .exec_block_tnx_processing
936 .observe(txn_processing_start.elapsed().as_secs_f64());
937 }
938
939 let post_processing_start = Instant::now();
941
942 let transaction_approvals_hashes = {
947 let approvals_checksum = types::compute_approvals_checksum(transaction_ids.clone())
948 .map_err(BlockExecutionError::FailedToComputeApprovalsChecksum)?;
949 let execution_results_checksum = compute_execution_results_checksum(
950 artifacts.iter().map(|artifact| &artifact.execution_result),
951 )?;
952 let mut checksum_registry = ChecksumRegistry::new();
953 checksum_registry.insert(APPROVALS_CHECKSUM_NAME, approvals_checksum);
954 checksum_registry.insert(EXECUTION_RESULTS_CHECKSUM_NAME, execution_results_checksum);
955
956 let mut effects = Effects::new();
957 effects.push(TransformV2::new(
958 Key::ChecksumRegistry,
959 TransformKindV2::Write(
960 CLValue::from_t(checksum_registry)
961 .map_err(BlockExecutionError::ChecksumRegistryToCLValue)?
962 .into(),
963 ),
964 ));
965 scratch_state.commit_effects(state_root_hash, effects)?;
966 transaction_ids
967 .into_iter()
968 .map(|id| id.approvals_hash())
969 .collect()
970 };
971
972 if let Some(metrics) = metrics.as_ref() {
973 metrics
974 .txn_approvals_hashes_calculation
975 .observe(post_processing_start.elapsed().as_secs_f64());
976 }
977
978 if let Some(rewards) = &executable_block.rewards {
985 let block_rewards_payout_start = Instant::now();
986 {
988 let fee_req = FeeRequest::new(
989 native_runtime_config.clone(),
990 state_root_hash,
991 protocol_version,
992 block_time,
993 );
994 debug!(?fee_req, "distributing fees");
995 match scratch_state.distribute_fees(fee_req) {
996 FeeResult::RootNotFound => {
997 return Err(BlockExecutionError::RootNotFound(state_root_hash));
998 }
999 FeeResult::Failure(fer) => return Err(BlockExecutionError::DistributeFees(fer)),
1000 FeeResult::Success {
1001 post_state_hash, ..
1002 } => {
1003 debug!("fee distribution success");
1004 state_root_hash = post_state_hash;
1005 }
1006 }
1007 }
1008
1009 let rewards_req = BlockRewardsRequest::new(
1010 native_runtime_config.clone(),
1011 state_root_hash,
1012 protocol_version,
1013 block_time,
1014 rewards.clone(),
1015 );
1016 debug!(?rewards_req, "distributing rewards");
1017 match scratch_state.distribute_block_rewards(rewards_req) {
1018 BlockRewardsResult::RootNotFound => {
1019 return Err(BlockExecutionError::RootNotFound(state_root_hash));
1020 }
1021 BlockRewardsResult::Failure(bre) => {
1022 return Err(BlockExecutionError::DistributeBlockRewards(bre));
1023 }
1024 BlockRewardsResult::Success {
1025 post_state_hash, ..
1026 } => {
1027 debug!("rewards distribution success");
1028 state_root_hash = post_state_hash;
1029 }
1030 }
1031 if let Some(metrics) = metrics.as_ref() {
1032 metrics
1033 .block_rewards_payout
1034 .observe(block_rewards_payout_start.elapsed().as_secs_f64());
1035 }
1036 }
1037
1038 let step_outcome = if let Some(era_report) = &executable_block.era_report {
1041 let step_processing_start = Instant::now();
1043
1044 debug!("committing step");
1045 let step_effects = match commit_step(
1046 native_runtime_config,
1047 &scratch_state,
1048 metrics.clone(),
1049 protocol_version,
1050 state_root_hash,
1051 era_report.clone(),
1052 block_time.value(),
1053 executable_block.era_id.successor(),
1054 ) {
1055 StepResult::RootNotFound => {
1056 return Err(BlockExecutionError::RootNotFound(state_root_hash));
1057 }
1058 StepResult::Failure(err) => return Err(BlockExecutionError::Step(err)),
1059 StepResult::Success {
1060 effects,
1061 post_state_hash,
1062 ..
1063 } => {
1064 state_root_hash = post_state_hash;
1065 effects
1066 }
1067 };
1068 debug!("step committed");
1069
1070 let era_validators_req = EraValidatorsRequest::new(state_root_hash);
1071 let era_validators_result = data_access_layer.era_validators(era_validators_req);
1072
1073 let upcoming_era_validators = match era_validators_result {
1074 EraValidatorsResult::RootNotFound => {
1075 panic!("root not found");
1076 }
1077 EraValidatorsResult::AuctionNotFound => {
1078 panic!("auction not found");
1079 }
1080 EraValidatorsResult::ValueNotFound(msg) => {
1081 panic!("validator snapshot not found: {}", msg);
1082 }
1083 EraValidatorsResult::Failure(tce) => {
1084 return Err(BlockExecutionError::GetEraValidators(tce));
1085 }
1086 EraValidatorsResult::Success { era_validators } => era_validators,
1087 };
1088
1089 if let Some(metrics) = metrics.as_ref() {
1091 metrics
1092 .exec_block_step_processing
1093 .observe(step_processing_start.elapsed().as_secs_f64());
1094 }
1095 Some(StepOutcome {
1096 step_effects,
1097 upcoming_era_validators,
1098 })
1099 } else {
1100 None
1101 };
1102
1103 if let Some(previous_block_height) = block_height.checked_sub(1) {
1106 if let Some(keys_to_prune) = calculate_prune_eras(
1107 activation_point_era_id,
1108 key_block_height_for_activation_point,
1109 previous_block_height,
1110 prune_batch_size,
1111 ) {
1112 let pruning_start = Instant::now();
1113
1114 let first_key = keys_to_prune.first().copied();
1115 let last_key = keys_to_prune.last().copied();
1116 info!(
1117 previous_block_height,
1118 %key_block_height_for_activation_point,
1119 %state_root_hash,
1120 first_key=?first_key,
1121 last_key=?last_key,
1122 "commit prune: preparing prune config"
1123 );
1124 let request = PruneRequest::new(state_root_hash, keys_to_prune);
1125 match scratch_state.prune(request) {
1126 PruneResult::RootNotFound => {
1127 error!(
1128 previous_block_height,
1129 %state_root_hash,
1130 "commit prune: root not found"
1131 );
1132 panic!(
1133 "Root {} not found while performing a prune.",
1134 state_root_hash
1135 );
1136 }
1137 PruneResult::MissingKey => {
1138 warn!(
1139 previous_block_height,
1140 %state_root_hash,
1141 "commit prune: key does not exist"
1142 );
1143 }
1144 PruneResult::Success {
1145 post_state_hash, ..
1146 } => {
1147 info!(
1148 previous_block_height,
1149 %key_block_height_for_activation_point,
1150 %state_root_hash,
1151 %post_state_hash,
1152 first_key=?first_key,
1153 last_key=?last_key,
1154 "commit prune: success"
1155 );
1156 state_root_hash = post_state_hash;
1157 }
1158 PruneResult::Failure(tce) => {
1159 error!(?tce, "commit prune: failure");
1160 return Err(tce.into());
1161 }
1162 }
1163 if let Some(metrics) = metrics.as_ref() {
1164 metrics
1165 .pruning_time
1166 .observe(pruning_start.elapsed().as_secs_f64());
1167 }
1168 }
1169 }
1170
1171 {
1172 let database_write_start = Instant::now();
1173 state_root_hash = data_access_layer.write_scratch_to_db(state_root_hash, scratch_state)?;
1176 if let Some(metrics) = metrics.as_ref() {
1177 metrics
1178 .scratch_lmdb_write_time
1179 .observe(database_write_start.elapsed().as_secs_f64());
1180 }
1181
1182 let database_flush_start = Instant::now();
1184 let flush_req = FlushRequest::new();
1185 let flush_result = data_access_layer.flush(flush_req);
1186 if let Err(gse) = flush_result.as_error() {
1187 error!("failed to flush lmdb");
1188 return Err(BlockExecutionError::Lmdb(gse));
1189 }
1190 if let Some(metrics) = metrics.as_ref() {
1191 metrics
1192 .database_flush_time
1193 .observe(database_flush_start.elapsed().as_secs_f64());
1194 }
1195 }
1196
1197 let next_era_id = executable_block.era_id.successor();
1199 let maybe_next_era_validator_weights: Option<(BTreeMap<PublicKey, U512>, u8)> =
1200 match step_outcome.as_ref() {
1201 None => None,
1202 Some(effects_and_validators) => {
1203 match effects_and_validators
1204 .upcoming_era_validators
1205 .get(&next_era_id)
1206 .cloned()
1207 {
1208 Some(validators) => next_era_gas_price.map(|gas_price| (validators, gas_price)),
1209 None => None,
1210 }
1211 }
1212 };
1213
1214 let era_end = match (
1215 executable_block.era_report,
1216 maybe_next_era_validator_weights,
1217 ) {
1218 (None, None) => None,
1219 (
1220 Some(InternalEraReport {
1221 equivocators,
1222 inactive_validators,
1223 }),
1224 Some((next_era_validator_weights, next_era_gas_price)),
1225 ) => Some(EraEndV2::new(
1226 equivocators,
1227 inactive_validators,
1228 next_era_validator_weights,
1229 executable_block.rewards.unwrap_or_default(),
1230 next_era_gas_price,
1231 )),
1232 (maybe_era_report, maybe_next_era_validator_weights) => {
1233 if maybe_era_report.is_none() {
1234 error!(
1235 "era_end {}: maybe_era_report is none",
1236 executable_block.era_id
1237 );
1238 }
1239 if maybe_next_era_validator_weights.is_none() {
1240 error!(
1241 "era_end {}: maybe_next_era_validator_weights is none",
1242 executable_block.era_id
1243 );
1244 }
1245 return Err(BlockExecutionError::FailedToCreateEraEnd {
1246 maybe_era_report,
1247 maybe_next_era_validator_weights,
1248 });
1249 }
1250 };
1251
1252 let block = Arc::new(BlockV2::new(
1253 parent_hash,
1254 parent_seed,
1255 state_root_hash,
1256 executable_block.random_bit,
1257 era_end,
1258 executable_block.timestamp,
1259 executable_block.era_id,
1260 block_height,
1261 protocol_version,
1262 (*proposer).clone(),
1263 executable_block.transaction_map,
1264 executable_block.rewarded_signatures,
1265 current_gas_price,
1266 last_switch_block_hash,
1267 ));
1268
1269 let proof_of_checksum_registry = match data_access_layer.tracking_copy(state_root_hash)? {
1270 Some(tc) => match tc.reader().read_with_proof(&Key::ChecksumRegistry)? {
1271 Some(proof) => proof,
1272 None => return Err(BlockExecutionError::MissingChecksumRegistry),
1273 },
1274 None => return Err(BlockExecutionError::RootNotFound(state_root_hash)),
1275 };
1276
1277 let approvals_hashes = Box::new(ApprovalsHashes::new(
1278 *block.hash(),
1279 transaction_approvals_hashes,
1280 proof_of_checksum_registry,
1281 ));
1282
1283 if let Some(metrics) = metrics.as_ref() {
1285 metrics
1286 .exec_block_post_processing
1287 .observe(post_processing_start.elapsed().as_secs_f64());
1288 metrics
1289 .exec_block_total
1290 .observe(start.elapsed().as_secs_f64());
1291 }
1292
1293 Ok(BlockAndExecutionArtifacts {
1294 block,
1295 approvals_hashes,
1296 execution_artifacts: artifacts,
1297 step_outcome,
1298 })
1299}
1300
1301pub(super) fn speculatively_execute<S>(
1306 state_provider: &S,
1307 chainspec: &Chainspec,
1308 execution_engine_v1: &ExecutionEngineV1,
1309 block_header: BlockHeader,
1310 input_transaction: Transaction,
1311) -> SpeculativeExecutionResult
1312where
1313 S: StateProvider,
1314{
1315 let transaction_config = &chainspec.transaction_config;
1316 let maybe_transaction = MetaTransaction::from_transaction(
1317 &input_transaction,
1318 chainspec.core_config.pricing_handling,
1319 transaction_config,
1320 );
1321 if let Err(error) = maybe_transaction {
1322 return SpeculativeExecutionResult::invalid_transaction(error);
1323 }
1324 let transaction = maybe_transaction.unwrap();
1325 let state_root_hash = block_header.state_root_hash();
1326 let parent_block_hash = block_header.block_hash();
1327 let block_height = block_header.height();
1328 let block_time = block_header
1329 .timestamp()
1330 .saturating_add(chainspec.core_config.minimum_block_time);
1331 let gas_limit = match input_transaction.gas_limit(chainspec, transaction.transaction_lane()) {
1332 Ok(gas_limit) => gas_limit,
1333 Err(_) => {
1334 return SpeculativeExecutionResult::invalid_gas_limit(input_transaction);
1335 }
1336 };
1337
1338 if transaction.is_deploy_transaction() {
1339 if transaction.is_native() {
1340 let limit = Gas::from(chainspec.system_costs_config.mint_costs().transfer);
1341 let protocol_version = chainspec.protocol_version();
1342 let native_runtime_config = NativeRuntimeConfig::from_chainspec(chainspec);
1343 let transaction_hash = transaction.hash();
1344 let initiator_addr = transaction.initiator_addr();
1345 let authorization_keys = transaction.authorization_keys();
1346 let runtime_args = match transaction.session_args().as_named() {
1347 Some(runtime_args) => runtime_args.clone(),
1348 None => {
1349 return SpeculativeExecutionResult::InvalidTransaction(InvalidTransaction::V1(
1350 InvalidTransactionV1::ExpectedNamedArguments,
1351 ));
1352 }
1353 };
1354
1355 let result = state_provider.transfer(TransferRequest::with_runtime_args(
1356 native_runtime_config.clone(),
1357 *state_root_hash,
1358 protocol_version,
1359 transaction_hash,
1360 initiator_addr.clone(),
1361 authorization_keys,
1362 runtime_args,
1363 ));
1364 SpeculativeExecutionResult::WasmV1(Box::new(utils::spec_exec_from_transfer_result(
1365 limit,
1366 result,
1367 block_header.block_hash(),
1368 )))
1369 } else {
1370 let block_info = BlockInfo::new(
1371 *state_root_hash,
1372 block_time.into(),
1373 parent_block_hash,
1374 block_height,
1375 execution_engine_v1.config().protocol_version(),
1376 );
1377 let session_input_data = transaction.to_session_input_data();
1378 let wasm_v1_result =
1379 match WasmV1Request::new_session(block_info, gas_limit, &session_input_data) {
1380 Ok(wasm_v1_request) => {
1381 execution_engine_v1.execute(state_provider, wasm_v1_request)
1382 }
1383 Err(error) => WasmV1Result::invalid_executable_item(gas_limit, error),
1384 };
1385 SpeculativeExecutionResult::WasmV1(Box::new(utils::spec_exec_from_wasm_v1_result(
1386 wasm_v1_result,
1387 block_header.block_hash(),
1388 )))
1389 }
1390 } else {
1391 SpeculativeExecutionResult::ReceivedV1Transaction
1392 }
1393}
1394
1395fn invoked_contract_will_pay(
1396 state_provider: &ScratchGlobalState,
1397 state_root_hash: Digest,
1398 transaction: &MetaTransaction,
1399) -> Result<Option<EntityAddr>, StateResultError> {
1400 let (hash_addr, entry_point_name) = match transaction.contract_direct_address() {
1401 None => {
1402 return Err(StateResultError::ValueNotFound(
1403 "contract direct address not found".to_string(),
1404 ))
1405 }
1406 Some((hash_addr, entry_point_name)) => (hash_addr, entry_point_name),
1407 };
1408 let entity_addr = EntityAddr::new_smart_contract(hash_addr);
1409 let entry_point_request = EntryPointRequest::new(state_root_hash, entry_point_name, hash_addr);
1410 let entry_point_response = state_provider.entry_point(entry_point_request);
1411 match entry_point_response {
1412 EntryPointResult::RootNotFound => Err(StateResultError::RootNotFound),
1413 EntryPointResult::ValueNotFound(msg) => Err(StateResultError::ValueNotFound(msg)),
1414 EntryPointResult::Failure(tce) => Err(StateResultError::Failure(tce)),
1415 EntryPointResult::Success { entry_point } => {
1416 if entry_point.will_pay_direct_invocation() {
1417 Ok(Some(entity_addr))
1418 } else {
1419 Ok(None)
1420 }
1421 }
1422 }
1423}
1424
1425#[allow(clippy::too_many_arguments)]
1426fn commit_step(
1427 native_runtime_config: NativeRuntimeConfig,
1428 scratch_state: &ScratchGlobalState,
1429 maybe_metrics: Option<Arc<Metrics>>,
1430 protocol_version: ProtocolVersion,
1431 state_hash: Digest,
1432 InternalEraReport {
1433 equivocators,
1434 inactive_validators,
1435 }: InternalEraReport,
1436 era_end_timestamp_millis: u64,
1437 next_era_id: EraId,
1438) -> StepResult {
1439 let evict_items = inactive_validators
1441 .into_iter()
1442 .chain(equivocators)
1443 .map(EvictItem::new)
1444 .collect();
1445
1446 let step_request = StepRequest::new(
1447 native_runtime_config,
1448 state_hash,
1449 protocol_version,
1450 vec![], evict_items,
1452 next_era_id,
1453 era_end_timestamp_millis,
1454 );
1455
1456 let start = Instant::now();
1458 let result = scratch_state.step(step_request);
1459 debug_assert!(result.is_success(), "{:?}", result);
1460 if let Some(metrics) = maybe_metrics {
1461 let elapsed = start.elapsed().as_secs_f64();
1462 metrics.commit_step.observe(elapsed);
1463 metrics.latest_commit_step.set(elapsed);
1464 }
1465 trace!(?result, "step response");
1466 result
1467}
1468
1469pub(crate) fn compute_execution_results_checksum<'a>(
1475 execution_results_iter: impl Iterator<Item = &'a ExecutionResult> + Clone,
1476) -> Result<Digest, BlockExecutionError> {
1477 let serialized_length = U32_SERIALIZED_LENGTH
1479 + execution_results_iter
1480 .clone()
1481 .map(|exec_result| exec_result.serialized_length())
1482 .sum::<usize>();
1483 let mut serialized = vec![];
1484 serialized
1485 .try_reserve_exact(serialized_length)
1486 .map_err(|_| {
1487 BlockExecutionError::FailedToComputeApprovalsChecksum(bytesrepr::Error::OutOfMemory)
1488 })?;
1489 let item_count: u32 = execution_results_iter
1490 .clone()
1491 .count()
1492 .try_into()
1493 .map_err(|_| {
1494 BlockExecutionError::FailedToComputeApprovalsChecksum(
1495 bytesrepr::Error::NotRepresentable,
1496 )
1497 })?;
1498 item_count
1499 .write_bytes(&mut serialized)
1500 .map_err(BlockExecutionError::FailedToComputeExecutionResultsChecksum)?;
1501 for execution_result in execution_results_iter {
1502 execution_result
1503 .write_bytes(&mut serialized)
1504 .map_err(BlockExecutionError::FailedToComputeExecutionResultsChecksum)?;
1505 }
1506
1507 serialized.hash().map_err(|_| {
1510 BlockExecutionError::FailedToComputeExecutionResultsChecksum(bytesrepr::Error::OutOfMemory)
1511 })
1512}