Skip to main content

sui_core/
authority.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::accumulators::coin_reservations::CachingCoinReservationResolver;
6use crate::accumulators::funds_read::AccountFundsRead;
7use crate::accumulators::object_funds_checker::ObjectFundsChecker;
8use crate::accumulators::object_funds_checker::metrics::ObjectFundsCheckerMetrics;
9use crate::accumulators::transaction_rewriting::rewrite_transaction_for_coin_reservations;
10use crate::accumulators::unsettled_object_withdrawals::UnsettledObjectWithdrawals;
11use crate::accumulators::{self, AccumulatorSettlementTxBuilder};
12use crate::checkpoints::CheckpointBuilderError;
13use crate::checkpoints::CheckpointBuilderResult;
14use crate::congestion_tracker::CongestionTracker;
15use crate::execution_cache::ExecutionCacheTraitPointers;
16use crate::execution_cache::TransactionCacheRead;
17use crate::execution_cache::writeback_cache::WritebackCache;
18use crate::execution_scheduler::ExecutionScheduler;
19use crate::execution_scheduler::funds_withdraw_scheduler::FundsSettlement;
20use crate::gasless_rate_limiter::ConsensusGaslessCounter;
21use crate::jsonrpc_index::CoinIndexKey2;
22use crate::traffic_controller::TrafficController;
23use crate::traffic_controller::metrics::TrafficControllerMetrics;
24use crate::transaction_deny_config_manager::TransactionDenyConfigManager;
25use crate::transaction_outputs::TransactionOutputs;
26use arc_swap::{ArcSwap, ArcSwapOption, Guard};
27use async_trait::async_trait;
28use authority_per_epoch_store::CertLockGuard;
29use dashmap::DashMap;
30use fastcrypto::encoding::Base58;
31use fastcrypto::encoding::Encoding;
32use fastcrypto::hash::MultisetHash;
33use itertools::Itertools;
34use move_binary_format::CompiledModule;
35use move_binary_format::binary_config::BinaryConfig;
36use move_core_types::annotated_value::MoveStructLayout;
37use move_core_types::language_storage::ModuleId;
38use mysten_common::ZipDebugEqIteratorExt;
39use mysten_common::{assert_reachable, fatal};
40use nonempty::NonEmpty;
41use parking_lot::Mutex;
42use prometheus::{
43    Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Registry,
44    register_histogram_vec_with_registry, register_histogram_with_registry,
45    register_int_counter_vec_with_registry, register_int_counter_with_registry,
46    register_int_gauge_vec_with_registry, register_int_gauge_with_registry,
47};
48use serde::de::DeserializeOwned;
49use serde::{Deserialize, Serialize};
50use shared_object_version_manager::AssignedVersions;
51use shared_object_version_manager::Schedulable;
52use std::collections::BTreeMap;
53use std::collections::BTreeSet;
54use std::fs::File;
55use std::io::Write;
56use std::path::{Path, PathBuf};
57use std::sync::atomic::Ordering;
58use std::time::Duration;
59use std::time::Instant;
60use std::time::SystemTime;
61use std::time::UNIX_EPOCH;
62use std::{
63    collections::{HashMap, HashSet},
64    fs,
65    pin::Pin,
66    str::FromStr,
67    sync::Arc,
68    vec,
69};
70use sui_config::NodeConfig;
71use sui_config::node::{AuthorityOverloadConfig, StateDebugDumpConfig};
72use sui_config::transaction_deny_config::TransactionDenyConfig;
73use sui_execution::Executor;
74use sui_protocol_config::PerObjectCongestionControlMode;
75use sui_types::accumulator_root::AccumulatorObjId;
76use sui_types::dynamic_field::visitor as DFV;
77use sui_types::execution::ExecutionOutput;
78use sui_types::execution::ExecutionTimeObservationKey;
79use sui_types::execution::ExecutionTiming;
80use sui_types::execution_params::ExecutionOrEarlyError;
81use sui_types::execution_params::FundsWithdrawStatus;
82use sui_types::execution_params::get_early_execution_error;
83use sui_types::inner_temporary_store::PackageStoreWithFallback;
84use sui_types::layout_resolver::LayoutResolver;
85use sui_types::layout_resolver::into_struct_layout;
86use sui_types::messages_consensus::AuthorityCapabilitiesV2;
87use sui_types::node_role::NodeRole;
88use sui_types::object::bounded_visitor::BoundedVisitor;
89use sui_types::storage::InputKey;
90use sui_types::storage::OverlayBackingPackageStore;
91use sui_types::storage::RuntimeObjectResolver;
92use sui_types::storage::TrackingBackingStore;
93use sui_types::traffic_control::{
94    PolicyConfig, RemoteFirewallConfig, TrafficControlReconfigParams,
95};
96use sui_types::transaction_executor::SimulateTransactionResult;
97use sui_types::transaction_executor::TransactionChecks;
98use sui_types::{SUI_ACCUMULATOR_ROOT_OBJECT_ID, accumulator_metadata};
99use tap::TapFallible;
100use tokio::sync::RwLock;
101use tokio::sync::mpsc::unbounded_channel;
102use tokio::sync::oneshot;
103use tokio::sync::watch::error::RecvError;
104use tokio::time::timeout;
105use tracing::{debug, error, info, instrument, warn};
106
107use self::authority_store::ExecutionLockWriteGuard;
108use self::authority_store_pruner::{AuthorityStorePruningMetrics, PrunerWatermarks};
109pub use authority_store::{AuthorityStore, ResolverWrapper};
110use mysten_metrics::{monitored_scope, spawn_monitored_task};
111
112use crate::jsonrpc_index::IndexStore;
113use crate::jsonrpc_index::{
114    CoinInfo, IndexStoreCacheUpdates, IndexStoreCacheUpdatesWithLocks, ObjectIndexChanges,
115};
116use mysten_common::debug_fatal;
117use shared_crypto::intent::{Intent, IntentScope};
118use sui_config::genesis::Genesis;
119use sui_config::node::{DBCheckpointConfig, ExpensiveSafetyCheckConfig};
120use sui_framework::{BuiltInFramework, SystemPackage};
121use sui_json_rpc_types::{
122    DevInspectResults, DryRunTransactionBlockResponse, EventFilter, SuiEvent, SuiMoveValue,
123    SuiObjectDataFilter, SuiTransactionBlockData, SuiTransactionBlockEffects,
124    SuiTransactionBlockEvents, TransactionFilter,
125};
126use sui_macros::{fail_point, fail_point_arg, fail_point_async, fail_point_if};
127use sui_rpc_store::Store as RpcStore;
128use sui_storage::key_value_store::{TransactionKeyValueStore, TransactionKeyValueStoreTrait};
129use sui_storage::key_value_store_metrics::KeyValueStoreMetrics;
130use sui_types::accumulator_root::AccumulatorValue;
131use sui_types::authenticator_state::get_authenticator_state;
132use sui_types::balance::Balance;
133use sui_types::coin_reservation;
134use sui_types::committee::{EpochId, ProtocolVersion};
135use sui_types::crypto::{AuthoritySignInfo, Signer};
136use sui_types::deny_list_v1::check_coin_deny_list_v1;
137use sui_types::digests::ChainIdentifier;
138use sui_types::dynamic_field::{DynamicFieldInfo, DynamicFieldName};
139use sui_types::effects::{
140    InputConsensusObject, SignedTransactionEffects, TransactionEffects, TransactionEffectsAPI,
141    TransactionEvents, VerifiedSignedTransactionEffects,
142};
143use sui_types::error::{ExecutionError, SuiErrorKind, UserInputError};
144use sui_types::event::EventID;
145use sui_types::executable_transaction::VerifiedExecutableTransaction;
146use sui_types::execution_status::ExecutionErrorKind;
147use sui_types::gas::{GasCostSummary, SuiGasStatus};
148use sui_types::inner_temporary_store::{InnerTemporaryStore, ObjectMap, TxCoins, WrittenObjects};
149use sui_types::message_envelope::Message;
150use sui_types::messages_checkpoint::{
151    CertifiedCheckpointSummary, CheckpointCommitment, CheckpointContents, CheckpointContentsDigest,
152    CheckpointDigest, CheckpointRequest, CheckpointRequestV2, CheckpointResponse,
153    CheckpointResponseV2, CheckpointSequenceNumber, CheckpointSummary, CheckpointSummaryResponse,
154    CheckpointTimestamp, ECMHLiveObjectSetDigest, VerifiedCheckpoint,
155};
156use sui_types::messages_grpc::{
157    LayoutGenerationOption, ObjectInfoRequest, ObjectInfoRequestKind, ObjectInfoResponse,
158    TransactionInfoRequest, TransactionInfoResponse, TransactionStatus,
159};
160use sui_types::metrics::{BytecodeVerifierMetrics, ExecutionMetrics};
161use sui_types::object::{MoveObject, OBJECT_START_VERSION, Owner, PastObjectRead};
162use sui_types::signature::GenericSignature;
163use sui_types::storage::{
164    BackingPackageStore, BackingStore, ObjectKey, ObjectOrTombstone, ObjectStore, WriteKind,
165};
166use sui_types::sui_system_state::SuiSystemStateTrait;
167use sui_types::sui_system_state::epoch_start_sui_system_state::EpochStartSystemStateTrait;
168use sui_types::sui_system_state::{SuiSystemState, get_sui_system_state};
169use sui_types::supported_protocol_versions::{ProtocolConfig, SupportedProtocolVersions};
170use sui_types::{
171    SUI_SYSTEM_ADDRESS,
172    base_types::*,
173    committee::Committee,
174    crypto::AuthoritySignature,
175    error::{SuiError, SuiResult},
176    object::{Object, ObjectRead},
177    transaction::*,
178};
179use sui_types::{TypeTag, is_system_package};
180use typed_store::TypedStoreError;
181use typed_store::rocks::StagedBatch;
182
183use crate::authority::authority_per_epoch_store::{AuthorityPerEpochStore, CertTxGuard};
184use crate::authority::authority_per_epoch_store_pruner::AuthorityPerEpochStorePruner;
185use crate::authority::authority_store::ExecutionLockReadGuard;
186use crate::authority::authority_store_pruner::{
187    AuthorityStorePruner, EPOCH_DURATION_MS_FOR_TESTING,
188};
189use crate::authority::epoch_start_configuration::EpochStartConfigTrait;
190use crate::authority::epoch_start_configuration::EpochStartConfiguration;
191use crate::checkpoints::CheckpointStore;
192use crate::epoch::committee_store::CommitteeStore;
193use crate::execution_cache::{
194    CheckpointCache, ExecutionCacheCommit, ExecutionCacheReconfigAPI, ExecutionCacheWrite,
195    ObjectCacheRead, StateSyncAPI,
196};
197use crate::execution_driver::execution_process;
198use crate::global_state_hasher::{GlobalStateHashStore, GlobalStateHasher, WrappedObject};
199use crate::metrics::LatencyObserver;
200use crate::metrics::RateTracker;
201use crate::module_cache_metrics::ResolverMetrics;
202use crate::overload_monitor::{AuthorityOverloadInfo, overload_monitor_accept_tx};
203use crate::stake_aggregator::StakeAggregator;
204use crate::subscription_handler::SubscriptionHandler;
205use crate::transaction_input_loader::TransactionInputLoader;
206
207#[cfg(msim)]
208pub use crate::checkpoints::checkpoint_executor::utils::{
209    CheckpointTimeoutConfig, init_checkpoint_timeout_config,
210};
211
212#[cfg(msim)]
213use sui_types::committee::CommitteeTrait;
214use sui_types::deny_list_v2::check_coin_deny_list_v2_during_signing;
215
216#[cfg(test)]
217#[path = "unit_tests/authority_tests.rs"]
218pub mod authority_tests;
219
220#[cfg(test)]
221#[path = "unit_tests/transaction_tests.rs"]
222pub mod transaction_tests;
223
224#[cfg(test)]
225#[path = "unit_tests/batch_transaction_tests.rs"]
226mod batch_transaction_tests;
227
228#[cfg(test)]
229#[path = "unit_tests/move_integration_tests.rs"]
230pub mod move_integration_tests;
231
232#[cfg(test)]
233#[path = "unit_tests/gas_tests.rs"]
234mod gas_tests;
235
236#[cfg(test)]
237#[path = "unit_tests/gas_data_tests.rs"]
238mod gas_data_tests;
239
240#[cfg(test)]
241#[path = "unit_tests/batch_verification_tests.rs"]
242mod batch_verification_tests;
243
244#[cfg(test)]
245#[path = "unit_tests/coin_deny_list_tests.rs"]
246mod coin_deny_list_tests;
247
248#[cfg(test)]
249#[path = "unit_tests/auth_unit_test_utils.rs"]
250pub mod auth_unit_test_utils;
251
252pub mod authority_test_utils;
253
254pub mod authority_per_epoch_store;
255pub mod authority_per_epoch_store_pruner;
256
257pub mod authority_store_pruner;
258pub mod authority_store_tables;
259pub mod authority_store_types;
260pub mod congestion_log;
261pub mod consensus_tx_status_cache;
262pub(crate) mod epoch_marker_key;
263pub mod epoch_start_configuration;
264pub mod execution_time_estimator;
265pub mod finalized_transactions_cache;
266pub mod shared_object_congestion_tracker;
267pub mod shared_object_version_manager;
268pub mod submitted_transaction_cache;
269pub mod test_authority_builder;
270pub mod transaction_deferral;
271pub mod transaction_reject_reason_cache;
272mod weighted_moving_average;
273
274pub(crate) mod authority_store;
275pub mod backpressure;
276
277/// Prometheus metrics which can be displayed in Grafana, queried and alerted on
278pub struct AuthorityMetrics {
279    tx_orders: IntCounter,
280    total_certs: IntCounter,
281    total_effects: IntCounter,
282    // TODO: this tracks consensus object tx, not just shared. Consider renaming.
283    pub shared_obj_tx: IntCounter,
284    sponsored_tx: IntCounter,
285    num_input_objs: Histogram,
286    // TODO: this tracks consensus object count, not just shared. Consider renaming.
287    num_shared_objects: Histogram,
288    batch_size: Histogram,
289
290    authority_state_handle_vote_transaction_latency: Histogram,
291
292    internal_execution_latency: Histogram,
293    /// Number of times the validator refused to report effects (signed or unsigned, labeled by
294    /// RPC surface) because it had previously signed different effects for the same transaction.
295    signed_effects_equivocation_prevented: IntCounterVec,
296    execution_load_input_objects_latency: Histogram,
297    prepare_certificate_latency: Histogram,
298    commit_certificate_latency: Histogram,
299    db_checkpoint_latency: Histogram,
300
301    // TODO: Rename these metrics.
302    pub(crate) transaction_manager_num_enqueued_certificates: IntCounterVec,
303    pub(crate) transaction_manager_num_pending_certificates: IntGauge,
304    pub(crate) transaction_manager_num_executing_certificates: IntGauge,
305    pub(crate) transaction_manager_transaction_queue_age_s: Histogram,
306
307    pub(crate) execution_driver_executed_transactions: IntCounter,
308    pub(crate) execution_driver_paused_transactions: IntCounter,
309    pub(crate) execution_driver_dispatch_queue: IntGauge,
310    pub(crate) execution_queueing_delay_s: Histogram,
311    pub(crate) prepare_cert_gas_latency_ratio: Histogram,
312    pub(crate) execution_gas_latency_ratio: Histogram,
313
314    pub(crate) skipped_consensus_txns: IntCounter,
315    pub(crate) skipped_consensus_txns_cache_hit: IntCounter,
316    pub(crate) consensus_handler_duplicate_tx_count: Histogram,
317
318    pub(crate) authority_overload_status: IntGauge,
319    pub(crate) authority_load_shedding_percentage: IntGauge,
320
321    pub(crate) transaction_overload_sources: IntCounterVec,
322
323    /// Post processing metrics
324    post_processing_total_events_emitted: IntCounter,
325    post_processing_total_tx_indexed: IntCounter,
326    post_processing_total_tx_had_event_processed: IntCounter,
327    post_processing_total_failures: IntCounter,
328
329    /// Consensus commit and transaction handler metrics
330    pub consensus_handler_processed: IntCounterVec,
331    pub consensus_handler_processed_user_transactions: IntCounterVec,
332    pub consensus_handler_transaction_sizes: HistogramVec,
333    pub consensus_handler_deferred_transactions: IntCounter,
334    pub consensus_handler_congested_transactions: IntCounter,
335    pub consensus_handler_unpaid_amplification_deferrals: IntCounter,
336    pub consensus_handler_double_spend_deferrals: IntCounter,
337    pub consensus_handler_double_spend_conflict_count: HistogramVec,
338    pub consensus_handler_double_spend_conflicting_authority: IntCounterVec,
339    pub consensus_handler_cancelled_transactions: IntCounter,
340    pub consensus_handler_dropped_transactions: IntCounterVec,
341    pub consensus_handler_max_object_costs: IntGaugeVec,
342    pub consensus_committed_subdags: IntCounterVec,
343    pub accumulator_deposits: IntCounter,
344    pub accumulator_withdrawals: IntCounter,
345    pub consensus_committed_messages: IntGaugeVec,
346    pub consensus_committed_user_transactions: IntGaugeVec,
347    pub consensus_finalized_user_transactions: IntGaugeVec,
348    pub consensus_rejected_user_transactions: IntGaugeVec,
349    pub consensus_calculated_throughput: IntGauge,
350    pub consensus_calculated_throughput_profile: IntGauge,
351    pub consensus_block_handler_block_processed: IntCounter,
352    pub consensus_block_handler_txn_processed: IntCounterVec,
353    pub consensus_block_handler_fastpath_executions: IntCounter,
354    pub consensus_timestamp_bias: Histogram,
355
356    pub execution_metrics: Arc<ExecutionMetrics>,
357
358    /// bytecode verifier metrics for tracking timeouts
359    pub bytecode_verifier_metrics: Arc<BytecodeVerifierMetrics>,
360
361    /// Count of zklogin signatures
362    pub zklogin_sig_count: IntCounter,
363    /// Count of multisig signatures
364    pub multisig_sig_count: IntCounter,
365
366    // Tracks recent average txn queueing delay between when it is ready for execution
367    // until it starts executing.
368    pub execution_queueing_latency: LatencyObserver,
369
370    // Tracks the rate of transactions become ready for execution in transaction manager.
371    // The need for the Mutex is that the tracker is updated in transaction manager and read
372    // in the overload_monitor. There should be low mutex contention because
373    // transaction manager is single threaded and the read rate in overload_monitor is
374    // low. In the case where transaction manager becomes multi-threaded, we can
375    // create one rate tracker per thread.
376    pub txn_ready_rate_tracker: Arc<Mutex<RateTracker>>,
377
378    // Tracks the rate of transactions starts execution in execution driver.
379    // Similar reason for using a Mutex here as to `txn_ready_rate_tracker`.
380    pub execution_rate_tracker: Arc<Mutex<RateTracker>>,
381}
382
383// Override default Prom buckets for positive numbers in 0-10M range
384const POSITIVE_INT_BUCKETS: &[f64] = &[
385    1., 2., 5., 7., 10., 20., 50., 70., 100., 200., 500., 700., 1000., 2000., 5000., 7000., 10000.,
386    20000., 50000., 70000., 100000., 200000., 500000., 700000., 1000000., 2000000., 5000000.,
387    7000000., 10000000.,
388];
389
390const LATENCY_SEC_BUCKETS: &[f64] = &[
391    0.0005, 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1., 2., 3., 4., 5., 6., 7., 8., 9.,
392    10., 20., 30., 60., 90.,
393];
394
395// Buckets for low latency samples. Starts from 10us.
396const LOW_LATENCY_SEC_BUCKETS: &[f64] = &[
397    0.00001, 0.00002, 0.00005, 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1,
398    0.2, 0.5, 1., 2., 5., 10., 20., 50., 100.,
399];
400
401// Buckets for consensus timestamp bias in seconds.
402// We expect 200-300ms, so we cluster the buckets more tightly in that range.
403const TIMESTAMP_BIAS_SEC_BUCKETS: &[f64] = &[
404    -2.0, 0.0, 0.2, 0.22, 0.24, 0.26, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.45, 0.50, 0.80,
405    2.0, 10.0, 30.0,
406];
407
408const GAS_LATENCY_RATIO_BUCKETS: &[f64] = &[
409    10.0, 50.0, 100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 2000.0,
410    3000.0, 4000.0, 5000.0, 6000.0, 7000.0, 8000.0, 9000.0, 10000.0, 50000.0, 100000.0, 1000000.0,
411];
412
413pub const DEV_INSPECT_GAS_COIN_VALUE: u64 = 1_000_000_000_000_000_000;
414
415// Transaction author should have observed the input objects as finalized output,
416// so usually the wait does not need to be long.
417// When submitted by TransactionDriver, it will retry quickly if there is no return from this validator too.
418pub const WAIT_FOR_FASTPATH_INPUT_TIMEOUT: Duration = Duration::from_secs(2);
419
420impl AuthorityMetrics {
421    pub fn new(registry: &prometheus::Registry) -> AuthorityMetrics {
422        Self {
423            tx_orders: register_int_counter_with_registry!(
424                "total_transaction_orders",
425                "Total number of transaction orders",
426                registry,
427            )
428            .unwrap(),
429            total_certs: register_int_counter_with_registry!(
430                "total_transaction_certificates",
431                "Total number of transaction certificates handled",
432                registry,
433            )
434            .unwrap(),
435            // total_effects == total transactions finished
436            total_effects: register_int_counter_with_registry!(
437                "total_transaction_effects",
438                "Total number of transaction effects produced",
439                registry,
440            )
441            .unwrap(),
442
443            shared_obj_tx: register_int_counter_with_registry!(
444                "num_shared_obj_tx",
445                "Number of transactions involving shared objects",
446                registry,
447            )
448            .unwrap(),
449
450            sponsored_tx: register_int_counter_with_registry!(
451                "num_sponsored_tx",
452                "Number of sponsored transactions",
453                registry,
454            )
455            .unwrap(),
456
457            num_input_objs: register_histogram_with_registry!(
458                "num_input_objects",
459                "Distribution of number of input TX objects per TX",
460                POSITIVE_INT_BUCKETS.to_vec(),
461                registry,
462            )
463            .unwrap(),
464            num_shared_objects: register_histogram_with_registry!(
465                "num_shared_objects",
466                "Number of shared input objects per TX",
467                POSITIVE_INT_BUCKETS.to_vec(),
468                registry,
469            )
470            .unwrap(),
471            batch_size: register_histogram_with_registry!(
472                "batch_size",
473                "Distribution of size of transaction batch",
474                POSITIVE_INT_BUCKETS.to_vec(),
475                registry,
476            )
477            .unwrap(),
478            authority_state_handle_vote_transaction_latency: register_histogram_with_registry!(
479                "authority_state_handle_vote_transaction_latency",
480                "Latency of voting on transactions without signing",
481                LATENCY_SEC_BUCKETS.to_vec(),
482                registry,
483            )
484            .unwrap(),
485            internal_execution_latency: register_histogram_with_registry!(
486                "authority_state_internal_execution_latency",
487                "Latency of actual certificate executions",
488                LATENCY_SEC_BUCKETS.to_vec(),
489                registry,
490            )
491            .unwrap(),
492            signed_effects_equivocation_prevented: register_int_counter_vec_with_registry!(
493                "authority_state_signed_effects_equivocation_prevented",
494                "Number of times the validator refused to report effects that differ from previously signed effects for the same transaction, by RPC surface",
495                &["surface"],
496                registry,
497            )
498            .unwrap(),
499            execution_load_input_objects_latency: register_histogram_with_registry!(
500                "authority_state_execution_load_input_objects_latency",
501                "Latency of loading input objects for execution",
502                LOW_LATENCY_SEC_BUCKETS.to_vec(),
503                registry,
504            )
505            .unwrap(),
506            prepare_certificate_latency: register_histogram_with_registry!(
507                "authority_state_prepare_certificate_latency",
508                "Latency of executing certificates, before committing the results",
509                LATENCY_SEC_BUCKETS.to_vec(),
510                registry,
511            )
512            .unwrap(),
513            commit_certificate_latency: register_histogram_with_registry!(
514                "authority_state_commit_certificate_latency",
515                "Latency of committing certificate execution results",
516                LATENCY_SEC_BUCKETS.to_vec(),
517                registry,
518            )
519            .unwrap(),
520            db_checkpoint_latency: register_histogram_with_registry!(
521                "db_checkpoint_latency",
522                "Latency of checkpointing dbs",
523                LATENCY_SEC_BUCKETS.to_vec(),
524                registry,
525            ).unwrap(),
526            transaction_manager_num_enqueued_certificates: register_int_counter_vec_with_registry!(
527                "transaction_manager_num_enqueued_certificates",
528                "Current number of certificates enqueued to ExecutionScheduler",
529                &["result"],
530                registry,
531            )
532            .unwrap(),
533            transaction_manager_num_pending_certificates: register_int_gauge_with_registry!(
534                "transaction_manager_num_pending_certificates",
535                "Number of certificates pending in ExecutionScheduler, with at least 1 missing input object",
536                registry,
537            )
538            .unwrap(),
539            transaction_manager_num_executing_certificates: register_int_gauge_with_registry!(
540                "transaction_manager_num_executing_certificates",
541                "Number of executing certificates, including queued and actually running certificates",
542                registry,
543            )
544            .unwrap(),
545            authority_overload_status: register_int_gauge_with_registry!(
546                "authority_overload_status",
547                "Whether authority is current experiencing overload and enters load shedding mode.",
548                registry)
549            .unwrap(),
550            authority_load_shedding_percentage: register_int_gauge_with_registry!(
551                "authority_load_shedding_percentage",
552                "The percentage of transactions is shed when the authority is in load shedding mode.",
553                registry)
554            .unwrap(),
555            transaction_manager_transaction_queue_age_s: register_histogram_with_registry!(
556                "transaction_manager_transaction_queue_age_s",
557                "Time spent in waiting for transaction in the queue",
558                LATENCY_SEC_BUCKETS.to_vec(),
559                registry,
560            )
561            .unwrap(),
562            transaction_overload_sources: register_int_counter_vec_with_registry!(
563                "transaction_overload_sources",
564                "Number of times each source indicates transaction overload.",
565                &["source"],
566                registry)
567            .unwrap(),
568            execution_driver_executed_transactions: register_int_counter_with_registry!(
569                "execution_driver_executed_transactions",
570                "Cumulative number of transaction executed by execution driver",
571                registry,
572            )
573            .unwrap(),
574            execution_driver_paused_transactions: register_int_counter_with_registry!(
575                "execution_driver_paused_transactions",
576                "Cumulative number of transactions paused by execution driver",
577                registry,
578            )
579            .unwrap(),
580            execution_driver_dispatch_queue: register_int_gauge_with_registry!(
581                "execution_driver_dispatch_queue",
582                "Number of transaction pending in execution driver dispatch queue",
583                registry,
584            )
585            .unwrap(),
586            execution_queueing_delay_s: register_histogram_with_registry!(
587                "execution_queueing_delay_s",
588                "Queueing delay between a transaction is ready for execution until it starts executing.",
589                LATENCY_SEC_BUCKETS.to_vec(),
590                registry
591            )
592            .unwrap(),
593            prepare_cert_gas_latency_ratio: register_histogram_with_registry!(
594                "prepare_cert_gas_latency_ratio",
595                "The ratio of computation gas divided by VM execution latency.",
596                GAS_LATENCY_RATIO_BUCKETS.to_vec(),
597                registry
598            )
599            .unwrap(),
600            execution_gas_latency_ratio: register_histogram_with_registry!(
601                "execution_gas_latency_ratio",
602                "The ratio of computation gas divided by certificate execution latency, include committing certificate.",
603                GAS_LATENCY_RATIO_BUCKETS.to_vec(),
604                registry
605            )
606            .unwrap(),
607            skipped_consensus_txns: register_int_counter_with_registry!(
608                "skipped_consensus_txns",
609                "Total number of consensus transactions skipped",
610                registry,
611            )
612            .unwrap(),
613            skipped_consensus_txns_cache_hit: register_int_counter_with_registry!(
614                "skipped_consensus_txns_cache_hit",
615                "Total number of consensus transactions skipped because of local cache hit",
616                registry,
617            )
618            .unwrap(),
619            consensus_handler_duplicate_tx_count: register_histogram_with_registry!(
620                "consensus_handler_duplicate_tx_count",
621                "Number of times each transaction appears in its first consensus commit",
622                POSITIVE_INT_BUCKETS.to_vec(),
623                registry,
624            )
625            .unwrap(),
626            post_processing_total_events_emitted: register_int_counter_with_registry!(
627                "post_processing_total_events_emitted",
628                "Total number of events emitted in post processing",
629                registry,
630            )
631            .unwrap(),
632            post_processing_total_tx_indexed: register_int_counter_with_registry!(
633                "post_processing_total_tx_indexed",
634                "Total number of txes indexed in post processing",
635                registry,
636            )
637            .unwrap(),
638            post_processing_total_tx_had_event_processed: register_int_counter_with_registry!(
639                "post_processing_total_tx_had_event_processed",
640                "Total number of txes finished event processing in post processing",
641                registry,
642            )
643            .unwrap(),
644            post_processing_total_failures: register_int_counter_with_registry!(
645                "post_processing_total_failures",
646                "Total number of failure in post processing",
647                registry,
648            )
649            .unwrap(),
650            consensus_handler_processed: register_int_counter_vec_with_registry!(
651                "consensus_handler_processed",
652                "Number of transactions processed by consensus handler, sliced by class and commit outcome (accepted/rejected)",
653                &["class", "outcome"],
654                registry
655            ).unwrap(),
656            consensus_handler_processed_user_transactions: register_int_counter_vec_with_registry!(
657                "consensus_handler_processed_user_transactions",
658                "Number of user transactions processed by consensus handler, sliced by commit outcome (accepted/rejected) and block author",
659                &["outcome", "authority"],
660                registry
661            ).unwrap(),
662            consensus_handler_transaction_sizes: register_histogram_vec_with_registry!(
663                "consensus_handler_transaction_sizes",
664                "Sizes of each type of transactions processed by consensus handler, sliced by class and commit outcome (accepted/rejected)",
665                &["class", "outcome"],
666                POSITIVE_INT_BUCKETS.to_vec(),
667                registry
668            ).unwrap(),
669            consensus_handler_deferred_transactions: register_int_counter_with_registry!(
670                "consensus_handler_deferred_transactions",
671                "Number of transactions deferred by consensus handler",
672                registry,
673            ).unwrap(),
674            consensus_handler_congested_transactions: register_int_counter_with_registry!(
675                "consensus_handler_congested_transactions",
676                "Number of transactions deferred by consensus handler due to congestion",
677                registry,
678            ).unwrap(),
679            consensus_handler_unpaid_amplification_deferrals: register_int_counter_with_registry!(
680                "consensus_handler_unpaid_amplification_deferrals",
681                "Number of transactions deferred due to unpaid consensus amplification",
682                registry,
683            ).unwrap(),
684            consensus_handler_double_spend_deferrals: register_int_counter_with_registry!(
685                "consensus_handler_double_spend_deferrals",
686                "Number of transactions deferred due to owned object double-spend contention",
687                registry,
688            ).unwrap(),
689            consensus_handler_double_spend_conflict_count: register_histogram_vec_with_registry!(
690                "consensus_handler_double_spend_conflict_count",
691                "Number of conflicting transactions per double-spend winner, by object type",
692                &["object_type"],
693                POSITIVE_INT_BUCKETS.to_vec(),
694                registry,
695            ).unwrap(),
696            consensus_handler_double_spend_conflicting_authority: register_int_counter_vec_with_registry!(
697                "consensus_handler_double_spend_conflicting_authority",
698                "Number of transactions involved in owned object double-spend contention, by the \
699                 block authority that sequenced the transaction and its role in the conflict \
700                 (winner = won the lock, loser = dropped)",
701                &["authority", "role"],
702                registry,
703            ).unwrap(),
704            consensus_handler_cancelled_transactions: register_int_counter_with_registry!(
705                "consensus_handler_cancelled_transactions",
706                "Number of transactions cancelled by consensus handler",
707                registry,
708            ).unwrap(),
709            consensus_handler_dropped_transactions: register_int_counter_vec_with_registry!(
710                "consensus_handler_dropped_transactions",
711                "Number of transactions dropped by consensus handler, by drop reason",
712                &["reason"],
713                registry,
714            ).unwrap(),
715            consensus_handler_max_object_costs: register_int_gauge_vec_with_registry!(
716                "consensus_handler_max_congestion_control_object_costs",
717                "Max object costs for congestion control in the current consensus commit",
718                &["commit_type"],
719                registry,
720            ).unwrap(),
721            consensus_committed_subdags: register_int_counter_vec_with_registry!(
722                "consensus_committed_subdags",
723                "Number of committed subdags, sliced by author",
724                &["authority"],
725                registry,
726            ).unwrap(),
727            accumulator_deposits: register_int_counter_with_registry!(
728                "accumulator_deposits",
729                "Total accumulator deposit (merge) events processed in settlement",
730                registry,
731            ).unwrap(),
732            accumulator_withdrawals: register_int_counter_with_registry!(
733                "accumulator_withdrawals",
734                "Total accumulator withdrawal (split) events processed in settlement",
735                registry,
736            ).unwrap(),
737            consensus_committed_messages: register_int_gauge_vec_with_registry!(
738                "consensus_committed_messages",
739                "Total number of committed consensus messages, sliced by author",
740                &["authority"],
741                registry,
742            ).unwrap(),
743            consensus_committed_user_transactions: register_int_gauge_vec_with_registry!(
744                "consensus_committed_user_transactions",
745                "Number of certified & user transactions committed, sliced by submitter and persisted across restarts within each epoch",
746                &["authority"],
747                registry,
748            ).unwrap(),
749            consensus_finalized_user_transactions: register_int_gauge_vec_with_registry!(
750                "consensus_finalized_user_transactions",
751                "Number of user transactions finalized, sliced by submitter",
752                &["authority"],
753                registry,
754            ).unwrap(),
755            consensus_rejected_user_transactions: register_int_gauge_vec_with_registry!(
756                "consensus_rejected_user_transactions",
757                "Number of user transactions rejected, sliced by submitter",
758                &["authority"],
759                registry,
760            ).unwrap(),
761            execution_metrics: Arc::new(ExecutionMetrics::new(registry)),
762            bytecode_verifier_metrics: Arc::new(BytecodeVerifierMetrics::new(registry)),
763            zklogin_sig_count: register_int_counter_with_registry!(
764                "zklogin_sig_count",
765                "Count of zkLogin signatures",
766                registry,
767            )
768            .unwrap(),
769            multisig_sig_count: register_int_counter_with_registry!(
770                "multisig_sig_count",
771                "Count of zkLogin signatures",
772                registry,
773            )
774            .unwrap(),
775            consensus_calculated_throughput: register_int_gauge_with_registry!(
776                "consensus_calculated_throughput",
777                "The calculated throughput from consensus output. Result is calculated based on unique transactions.",
778                registry,
779            ).unwrap(),
780            consensus_calculated_throughput_profile: register_int_gauge_with_registry!(
781                "consensus_calculated_throughput_profile",
782                "The current active calculated throughput profile",
783                registry
784            ).unwrap(),
785            consensus_block_handler_block_processed: register_int_counter_with_registry!(
786                "consensus_block_handler_block_processed",
787                "Number of blocks processed by consensus block handler.",
788                registry
789            ).unwrap(),
790            consensus_block_handler_txn_processed: register_int_counter_vec_with_registry!(
791                "consensus_block_handler_txn_processed",
792                "Number of transactions processed by consensus block handler, by whether they are certified or rejected.",
793                &["outcome"],
794                registry
795            ).unwrap(),
796            consensus_block_handler_fastpath_executions: register_int_counter_with_registry!(
797                "consensus_block_handler_fastpath_executions",
798                "Number of fastpath transactions sent for execution by consensus transaction handler",
799                registry,
800            ).unwrap(),
801            consensus_timestamp_bias: register_histogram_with_registry!(
802                "consensus_timestamp_bias",
803                "Bias/delay from consensus timestamp to system time",
804                TIMESTAMP_BIAS_SEC_BUCKETS.to_vec(),
805                registry
806            ).unwrap(),
807            execution_queueing_latency: LatencyObserver::new(),
808            txn_ready_rate_tracker: Arc::new(Mutex::new(RateTracker::new(Duration::from_secs(10)))),
809            execution_rate_tracker: Arc::new(Mutex::new(RateTracker::new(Duration::from_secs(10)))),
810        }
811    }
812}
813
814/// a Trait object for `Signer` that is:
815/// - Pin, i.e. confined to one place in memory (we don't want to copy private keys).
816/// - Sync, i.e. can be safely shared between threads.
817///
818/// Typically instantiated with Box::pin(keypair) where keypair is a `KeyPair`
819///
820pub type StableSyncAuthoritySigner = Pin<Arc<dyn Signer<AuthoritySignature> + Send + Sync>>;
821
822/// The expected effects digest of a transaction, when the effects are known before execution,
823/// tagged with where the expectation came from.
824#[derive(Debug, Clone, Copy, PartialEq, Eq)]
825pub enum ExpectedEffectsDigest {
826    /// Read from the contents of the certified checkpoint at `checkpoint_seq`.
827    Certified {
828        digest: TransactionEffectsDigest,
829        checkpoint_seq: CheckpointSequenceNumber,
830    },
831    /// Known but not network-certified: this validator's own previously signed effects, or an
832    /// operator override.
833    Uncertified(TransactionEffectsDigest),
834}
835
836impl ExpectedEffectsDigest {
837    pub fn digest(&self) -> TransactionEffectsDigest {
838        match self {
839            Self::Certified { digest, .. } => *digest,
840            Self::Uncertified(digest) => *digest,
841        }
842    }
843
844    pub fn checkpoint_seq(&self) -> Option<CheckpointSequenceNumber> {
845        match self {
846            Self::Certified { checkpoint_seq, .. } => Some(*checkpoint_seq),
847            Self::Uncertified(_) => None,
848        }
849    }
850}
851
852/// Execution env contains the "environment" for the transaction to be executed in, that is,
853/// all the information necessary for execution that is not specified by the transaction itself.
854#[derive(Debug, Clone)]
855pub struct ExecutionEnv {
856    /// The assigned version of each shared object for the transaction.
857    pub assigned_versions: AssignedVersions,
858    /// The expected digest of the effects of the transaction, if executing from checkpoint or
859    /// other sources where the effects are known in advance.
860    pub expected_effects_digest: Option<ExpectedEffectsDigest>,
861    /// Status of the address funds withdraw scheduling of the transaction,
862    /// including both address and object funds withdraws.
863    pub funds_withdraw_status: FundsWithdrawStatus,
864    /// Transactions that must finish before this transaction can be executed.
865    /// Used to schedule barrier transactions after non-exclusive writes.
866    pub barrier_dependencies: Vec<TransactionDigest>,
867}
868
869impl Default for ExecutionEnv {
870    fn default() -> Self {
871        Self {
872            assigned_versions: Default::default(),
873            expected_effects_digest: None,
874            funds_withdraw_status: FundsWithdrawStatus::MaybeSufficient,
875            barrier_dependencies: Default::default(),
876        }
877    }
878}
879
880impl ExecutionEnv {
881    pub fn new() -> Self {
882        Default::default()
883    }
884
885    pub fn with_expected_effects(mut self, expected: ExpectedEffectsDigest) -> Self {
886        self.expected_effects_digest = Some(expected);
887        self
888    }
889
890    pub fn with_assigned_versions(mut self, assigned_versions: AssignedVersions) -> Self {
891        self.assigned_versions = assigned_versions;
892        self
893    }
894
895    pub fn with_insufficient_funds(mut self) -> Self {
896        self.funds_withdraw_status = FundsWithdrawStatus::Insufficient;
897        self
898    }
899
900    pub fn with_barrier_dependencies(
901        mut self,
902        barrier_dependencies: BTreeSet<TransactionDigest>,
903    ) -> Self {
904        self.barrier_dependencies = barrier_dependencies.into_iter().collect();
905        self
906    }
907}
908
909#[derive(Debug)]
910pub struct ForkRecoveryState {
911    /// Transaction digest to effects digest overrides
912    transaction_overrides:
913        parking_lot::RwLock<HashMap<TransactionDigest, TransactionEffectsDigest>>,
914}
915
916impl Default for ForkRecoveryState {
917    fn default() -> Self {
918        Self {
919            transaction_overrides: parking_lot::RwLock::new(HashMap::new()),
920        }
921    }
922}
923
924impl ForkRecoveryState {
925    pub fn new(config: Option<&sui_config::node::ForkRecoveryConfig>) -> Result<Self, SuiError> {
926        let Some(config) = config else {
927            return Ok(Self::default());
928        };
929
930        let mut transaction_overrides = HashMap::new();
931        for (tx_digest_str, effects_digest_str) in &config.transaction_overrides {
932            let tx_digest = TransactionDigest::from_str(tx_digest_str).map_err(|_| {
933                SuiErrorKind::Unknown(format!("Invalid transaction digest: {}", tx_digest_str))
934            })?;
935            let effects_digest =
936                TransactionEffectsDigest::from_str(effects_digest_str).map_err(|_| {
937                    SuiErrorKind::Unknown(format!("Invalid effects digest: {}", effects_digest_str))
938                })?;
939            transaction_overrides.insert(tx_digest, effects_digest);
940        }
941
942        Ok(Self {
943            transaction_overrides: parking_lot::RwLock::new(transaction_overrides),
944        })
945    }
946
947    pub fn get_transaction_override(
948        &self,
949        tx_digest: &TransactionDigest,
950    ) -> Option<TransactionEffectsDigest> {
951        self.transaction_overrides.read().get(tx_digest).copied()
952    }
953}
954
955pub type PostProcessingOutput = (StagedBatch, IndexStoreCacheUpdates);
956
957pub struct AuthorityState {
958    // Fixed size, static, identity of the authority
959    /// The name of this authority.
960    pub name: AuthorityName,
961    /// The signature key of the authority.
962    pub secret: StableSyncAuthoritySigner,
963
964    /// The database
965    input_loader: TransactionInputLoader,
966    execution_cache_trait_pointers: ExecutionCacheTraitPointers,
967    coin_reservation_resolver: Arc<CachingCoinReservationResolver>,
968
969    epoch_store: ArcSwap<AuthorityPerEpochStore>,
970
971    /// This lock denotes current 'execution epoch'.
972    /// Execution acquires read lock, checks certificate epoch and holds it until all writes are complete.
973    /// Reconfiguration acquires write lock, changes the epoch and revert all transactions
974    /// from previous epoch that are executed but did not make into checkpoint.
975    execution_lock: RwLock<EpochId>,
976
977    pub indexes: Option<Arc<IndexStore>>,
978
979    pub subscription_handler: Arc<SubscriptionHandler>,
980    pub checkpoint_store: Arc<CheckpointStore>,
981
982    committee_store: Arc<CommitteeStore>,
983
984    /// Schedules transaction execution.
985    execution_scheduler: Arc<ExecutionScheduler>,
986
987    /// Shuts down the execution task. Used only in testing.
988    #[allow(unused)]
989    tx_execution_shutdown: Mutex<Option<oneshot::Sender<()>>>,
990
991    pub metrics: Arc<AuthorityMetrics>,
992    _pruner: AuthorityStorePruner,
993    _authority_per_epoch_pruner: AuthorityPerEpochStorePruner,
994
995    /// Take db checkpoints of different dbs
996    db_checkpoint_config: DBCheckpointConfig,
997
998    pub config: NodeConfig,
999
1000    /// Current overload status in this authority. Updated periodically.
1001    pub overload_info: AuthorityOverloadInfo,
1002
1003    /// The chain identifier is derived from the digest of the genesis checkpoint.
1004    chain_identifier: ChainIdentifier,
1005
1006    pub(crate) congestion_tracker: Arc<CongestionTracker>,
1007
1008    /// Consumed by gasless tx rate limiter.
1009    pub(crate) consensus_gasless_counter: Arc<ConsensusGaslessCounter>,
1010
1011    /// Traffic controller for Sui core servers (json-rpc, validator service)
1012    pub traffic_controller: Option<Arc<TrafficController>>,
1013
1014    /// Fork recovery state for handling equivocation after forks
1015    fork_recovery_state: Option<ForkRecoveryState>,
1016
1017    /// Notification channel for reconfiguration
1018    notify_epoch: tokio::sync::watch::Sender<EpochId>,
1019
1020    pub(crate) object_funds_checker: ArcSwapOption<ObjectFundsChecker>,
1021    object_funds_checker_metrics: Arc<ObjectFundsCheckerMetrics>,
1022    pub(crate) unsettled_object_withdrawals: Arc<UnsettledObjectWithdrawals>,
1023
1024    /// Tracks transactions whose post-processing (indexing/events) is still in flight.
1025    /// CheckpointExecutor removes entries and collects the index batches before committing
1026    /// them atomically at checkpoint boundaries.
1027    pending_post_processing:
1028        Arc<DashMap<TransactionDigest, oneshot::Receiver<PostProcessingOutput>>>,
1029
1030    /// Limits the number of concurrent post-processing tasks to avoid overwhelming
1031    /// the blocking thread pool. Defaults to the number of available CPUs.
1032    post_processing_semaphore: Arc<tokio::sync::Semaphore>,
1033
1034    /// Created once per process, then re-attached to each new `AuthorityPerEpochStore`
1035    /// at reconfiguration.
1036    transaction_deny_config_manager: Arc<TransactionDenyConfigManager>,
1037}
1038
1039/// The authority state encapsulates all state, drives execution, and ensures safety.
1040///
1041/// Note the authority operations can be accessed through a read ref (&) and do not
1042/// require &mut. Internally a database is synchronized through a mutex lock.
1043///
1044/// Repeating valid commands should produce no changes and return no error.
1045impl AuthorityState {
1046    pub fn node_role(&self, epoch_store: &AuthorityPerEpochStore) -> NodeRole {
1047        epoch_store.node_role()
1048    }
1049
1050    pub fn is_validator(&self, epoch_store: &AuthorityPerEpochStore) -> bool {
1051        epoch_store.node_role().is_validator()
1052    }
1053
1054    pub fn is_fullnode(&self, epoch_store: &AuthorityPerEpochStore) -> bool {
1055        epoch_store.node_role().is_fullnode()
1056    }
1057
1058    pub fn committee_store(&self) -> &Arc<CommitteeStore> {
1059        &self.committee_store
1060    }
1061
1062    pub fn clone_committee_store(&self) -> Arc<CommitteeStore> {
1063        self.committee_store.clone()
1064    }
1065
1066    pub fn overload_config(&self) -> &AuthorityOverloadConfig {
1067        &self.config.authority_overload_config
1068    }
1069
1070    pub fn get_epoch_state_commitments(
1071        &self,
1072        epoch: EpochId,
1073    ) -> SuiResult<Option<Vec<CheckpointCommitment>>> {
1074        self.checkpoint_store.get_epoch_state_commitments(epoch)
1075    }
1076
1077    /// Runs deny list checks and processes funds withdrawals. Called before loading input
1078    /// objects, since these checks don't depend on object state.
1079    fn pre_object_load_checks(
1080        &self,
1081        tx_data: &TransactionData,
1082        tx_signatures: &[GenericSignature],
1083        input_object_kinds: &[InputObjectKind],
1084        receiving_objects_refs: &[ObjectRef],
1085        protocol_config: &ProtocolConfig,
1086    ) -> SuiResult<BTreeMap<AccumulatorObjId, (u64, TypeTag, SuiAddress)>> {
1087        // Note: the deny checks may do redundant package loads but:
1088        // - they only load packages when there is an active package deny map
1089        // - the loads are cached anyway
1090        let deny_config = self
1091            .transaction_deny_config_manager
1092            .effective_config()
1093            .load();
1094        sui_transaction_checks::deny::check_transaction_for_signing(
1095            tx_data,
1096            tx_signatures,
1097            input_object_kinds,
1098            receiving_objects_refs,
1099            &deny_config,
1100            self.get_backing_package_store().as_ref(),
1101        )?;
1102
1103        let declared_withdrawals = tx_data.process_funds_withdrawals_for_signing(
1104            self.chain_identifier,
1105            self.coin_reservation_resolver.as_ref(),
1106        )?;
1107
1108        self.execution_cache_trait_pointers
1109            .account_funds_read
1110            .check_amounts_available(&declared_withdrawals)?;
1111
1112        if protocol_config.gasless_verify_remaining_balance() && tx_data.is_gasless_transaction() {
1113            let min_amounts =
1114                sui_types::transaction::get_gasless_allowed_token_types(protocol_config);
1115            self.execution_cache_trait_pointers
1116                .account_funds_read
1117                .check_remaining_amounts_after_withdrawal(&declared_withdrawals, &min_amounts)?;
1118        }
1119
1120        Ok(declared_withdrawals)
1121    }
1122
1123    fn handle_transaction_deny_checks(
1124        &self,
1125        transaction: &VerifiedTransaction,
1126        epoch_store: &Arc<AuthorityPerEpochStore>,
1127    ) -> SuiResult<CheckedInputObjects> {
1128        let tx_digest = transaction.digest();
1129        let tx_data = transaction.data().transaction_data();
1130
1131        let input_object_kinds = tx_data.input_objects()?;
1132        let receiving_objects_refs = tx_data.receiving_objects();
1133
1134        self.pre_object_load_checks(
1135            tx_data,
1136            transaction.tx_signatures(),
1137            &input_object_kinds,
1138            &receiving_objects_refs,
1139            epoch_store.protocol_config(),
1140        )?;
1141
1142        let (input_objects, receiving_objects) = self.input_loader.read_objects_for_signing(
1143            Some(tx_digest),
1144            &input_object_kinds,
1145            &receiving_objects_refs,
1146            epoch_store.epoch(),
1147        )?;
1148
1149        let (_gas_status, checked_input_objects) = sui_transaction_checks::check_transaction_input(
1150            epoch_store.protocol_config(),
1151            epoch_store.reference_gas_price(),
1152            tx_data,
1153            input_objects,
1154            &receiving_objects,
1155            &self.metrics.bytecode_verifier_metrics,
1156            &self.config.verifier_signing_config,
1157        )?;
1158
1159        self.handle_coin_deny_list_checks(
1160            tx_data,
1161            &checked_input_objects,
1162            &receiving_objects,
1163            epoch_store,
1164        )?;
1165
1166        Ok(checked_input_objects)
1167    }
1168
1169    fn handle_coin_deny_list_checks(
1170        &self,
1171        tx_data: &TransactionData,
1172        checked_input_objects: &CheckedInputObjects,
1173        receiving_objects: &ReceivingObjects,
1174        epoch_store: &Arc<AuthorityPerEpochStore>,
1175    ) -> SuiResult<()> {
1176        use sui_types::balance::Balance;
1177
1178        let declared_withdrawals = tx_data.process_funds_withdrawals_for_signing(
1179            self.chain_identifier,
1180            self.coin_reservation_resolver.as_ref(),
1181        )?;
1182
1183        let funds_withdraw_types = declared_withdrawals
1184            .values()
1185            .filter_map(|(_, type_tag, _)| {
1186                Balance::maybe_get_balance_type_param(type_tag)
1187                    .map(|ty| ty.to_canonical_string(false))
1188            })
1189            .collect::<BTreeSet<_>>();
1190
1191        if epoch_store.coin_deny_list_v1_enabled() {
1192            check_coin_deny_list_v1(
1193                tx_data.sender(),
1194                checked_input_objects,
1195                receiving_objects,
1196                funds_withdraw_types.clone(),
1197                &self.get_object_store(),
1198            )?;
1199        }
1200
1201        if epoch_store.protocol_config().enable_coin_deny_list_v2() {
1202            check_coin_deny_list_v2_during_signing(
1203                tx_data.sender(),
1204                checked_input_objects,
1205                receiving_objects,
1206                funds_withdraw_types.clone(),
1207                &self.get_object_store(),
1208            )?;
1209        }
1210
1211        Ok(())
1212    }
1213
1214    /// Vote for a transaction, either when validator receives a submit_transaction request,
1215    /// or sees a transaction from consensus. Performs the same types of checks as
1216    /// transaction signing, but does not explicitly sign the transaction.
1217    /// Note that if the transaction has been executed, we still go through the
1218    /// same checks. If the transaction has only been executed through mysticeti fastpath,
1219    /// but not yet finalized, the signing will still work since the objects are still available.
1220    /// But if the transaction has been finalized, the signing will fail.
1221    /// TODO(mysticeti-fastpath): Assess whether we want to optimize the case when the transaction
1222    /// has already been finalized executed.
1223    #[instrument(level = "trace", skip_all, fields(tx_digest = ?transaction.digest()))]
1224    pub fn handle_vote_transaction(
1225        &self,
1226        epoch_store: &Arc<AuthorityPerEpochStore>,
1227        transaction: VerifiedTransaction,
1228    ) -> SuiResult<()> {
1229        debug!("handle_vote_transaction");
1230
1231        let _metrics_guard = self
1232            .metrics
1233            .authority_state_handle_vote_transaction_latency
1234            .start_timer();
1235        self.metrics.tx_orders.inc();
1236
1237        // The should_accept_user_certs check here is best effort, because
1238        // between a validator signs a tx and a cert is formed, the validator
1239        // could close the window.
1240        if !epoch_store
1241            .get_reconfig_state_read_lock_guard()
1242            .should_accept_user_certs()
1243        {
1244            return Err(SuiErrorKind::ValidatorHaltedAtEpochEnd.into());
1245        }
1246
1247        // Accept finalized transactions, instead of voting to reject them.
1248        // Checking executed transactions is limited to the current epoch.
1249        // Otherwise there can be a race where the transaction is accepted because it has executed,
1250        // but the executed effects are pruned post consensus, leading to failures.
1251        let tx_digest = *transaction.digest();
1252        if epoch_store.is_recently_finalized(&tx_digest)
1253            || epoch_store.transactions_executed_in_cur_epoch(&[tx_digest])?[0]
1254        {
1255            assert_reachable!("transaction recently executed");
1256            return Ok(());
1257        }
1258
1259        if self
1260            .get_transaction_cache_reader()
1261            .transaction_executed_in_last_epoch(transaction.digest(), epoch_store.epoch())
1262        {
1263            return Err(SuiErrorKind::TransactionAlreadyExecuted {
1264                digest: (*transaction.digest()),
1265            }
1266            .into());
1267        }
1268
1269        // Ensure that validator cannot reconfigure while we are validating the transaction.
1270        let _execution_lock = self.execution_lock_for_validation()?;
1271
1272        let checked_input_objects =
1273            self.handle_transaction_deny_checks(&transaction, epoch_store)?;
1274
1275        let owned_objects = checked_input_objects.inner().filter_owned_objects();
1276
1277        // Validate owned object versions without acquiring locks. Locking happens post-consensus
1278        // in the consensus handler. Validation still runs to prevent spam transactions with
1279        // invalid object versions, and is necessary to handle recently created objects.
1280        self.get_cache_writer()
1281            .validate_owned_object_versions(&owned_objects)
1282    }
1283
1284    /// Used for early client validation check for transactions before submission to server.
1285    /// Performs the same validation checks as handle_vote_transaction without acquiring locks.
1286    /// This allows for fast failure feedback to clients for non-retriable errors.
1287    ///
1288    /// The key addition is checking that owned object versions match live object versions.
1289    /// This is necessary because handle_transaction_deny_checks fetches objects at their
1290    /// requested version (which may exist in storage as historical versions), whereas
1291    /// validators check against the live/current version during locking (verify_live_object).
1292    pub fn check_transaction_validity(
1293        &self,
1294        epoch_store: &Arc<AuthorityPerEpochStore>,
1295        transaction: &VerifiedTransaction,
1296        enforce_live_input_objects: bool,
1297    ) -> SuiResult<()> {
1298        if !epoch_store
1299            .get_reconfig_state_read_lock_guard()
1300            .should_accept_user_certs()
1301        {
1302            return Err(SuiErrorKind::ValidatorHaltedAtEpochEnd.into());
1303        }
1304
1305        transaction.validity_check(&epoch_store.tx_validity_check_context())?;
1306
1307        let checked_input_objects =
1308            self.handle_transaction_deny_checks(transaction, epoch_store)?;
1309
1310        // Check that owned object versions match live objects
1311        let owned_objects = checked_input_objects.inner().filter_owned_objects();
1312        let cache_reader = self.get_object_cache_reader();
1313
1314        for obj_ref in &owned_objects {
1315            if let Some(live_object) = cache_reader.get_object(&obj_ref.0) {
1316                // Only reject if transaction references an old version. Allow newer
1317                // versions that may exist on validators but not yet synced to fullnode.
1318                if obj_ref.1 < live_object.version() {
1319                    return Err(SuiErrorKind::UserInputError {
1320                        error: UserInputError::ObjectVersionUnavailableForConsumption {
1321                            provided_obj_ref: *obj_ref,
1322                            current_version: live_object.version(),
1323                        },
1324                    }
1325                    .into());
1326                }
1327
1328                if enforce_live_input_objects && live_object.version() < obj_ref.1 {
1329                    // Returns a non-retriable error when the input object version is required to be live.
1330                    return Err(SuiErrorKind::UserInputError {
1331                        error: UserInputError::ObjectVersionUnavailableForConsumption {
1332                            provided_obj_ref: *obj_ref,
1333                            current_version: live_object.version(),
1334                        },
1335                    }
1336                    .into());
1337                }
1338
1339                // If version matches, verify digest also matches
1340                if obj_ref.1 == live_object.version() && obj_ref.2 != live_object.digest() {
1341                    return Err(SuiErrorKind::UserInputError {
1342                        error: UserInputError::InvalidObjectDigest {
1343                            object_id: obj_ref.0,
1344                            expected_digest: live_object.digest(),
1345                        },
1346                    }
1347                    .into());
1348                }
1349            }
1350        }
1351
1352        Ok(())
1353    }
1354
1355    pub fn check_system_overload_at_signing(&self) -> bool {
1356        self.config
1357            .authority_overload_config
1358            .check_system_overload_at_signing
1359    }
1360
1361    pub(crate) fn check_system_overload(
1362        &self,
1363        tx_data: &SenderSignedData,
1364        do_authority_overload_check: bool,
1365    ) -> SuiResult {
1366        if do_authority_overload_check {
1367            self.check_authority_overload(tx_data).tap_err(|_| {
1368                self.update_overload_metrics("execution_queue");
1369            })?;
1370        }
1371        self.execution_scheduler
1372            .check_execution_overload(self.overload_config(), tx_data)
1373            .tap_err(|_| {
1374                self.update_overload_metrics("execution_pending");
1375            })?;
1376        // Consensus overload is handled by the admission queue in authority_server.rs.
1377
1378        let pending_tx_count = self
1379            .get_cache_commit()
1380            .approximate_pending_transaction_count();
1381        if pending_tx_count > self.config.execution_cache.backpressure_threshold_for_rpc() {
1382            return Err(SuiErrorKind::ValidatorOverloadedRetryAfter {
1383                retry_after_secs: 10,
1384            }
1385            .into());
1386        }
1387
1388        Ok(())
1389    }
1390
1391    fn check_authority_overload(&self, tx_data: &SenderSignedData) -> SuiResult {
1392        if !self.overload_info.is_overload.load(Ordering::Relaxed) {
1393            return Ok(());
1394        }
1395
1396        let load_shedding_percentage = self
1397            .overload_info
1398            .load_shedding_percentage
1399            .load(Ordering::Relaxed);
1400        overload_monitor_accept_tx(load_shedding_percentage, tx_data.digest())
1401    }
1402
1403    pub(crate) fn update_overload_metrics(&self, source: &str) {
1404        self.metrics
1405            .transaction_overload_sources
1406            .with_label_values(&[source])
1407            .inc();
1408    }
1409
1410    /// Waits for fastpath (owned, package) dependency objects to become available.
1411    /// Returns true if a chosen set of fastpath dependency objects are available,
1412    /// returns false otherwise after an internal timeout.
1413    pub(crate) async fn wait_for_fastpath_dependency_objects(
1414        &self,
1415        transaction: &VerifiedTransaction,
1416        epoch: EpochId,
1417    ) -> SuiResult<bool> {
1418        let txn_data = transaction.data().transaction_data();
1419        let (move_objects, packages, receiving_objects) = txn_data.fastpath_dependency_objects()?;
1420
1421        // Gather and filter input objects to wait for.
1422        let fastpath_dependency_objects: Vec<_> = move_objects
1423            .into_iter()
1424            .filter_map(|obj_ref| self.should_wait_for_dependency_object(obj_ref))
1425            .chain(
1426                packages
1427                    .into_iter()
1428                    .map(|package_id| InputKey::Package { id: package_id }),
1429            )
1430            .collect();
1431        let receiving_keys: HashSet<_> = receiving_objects
1432            .into_iter()
1433            .filter_map(|receiving_obj_ref| {
1434                self.should_wait_for_dependency_object(receiving_obj_ref)
1435            })
1436            .collect();
1437        if fastpath_dependency_objects.is_empty() && receiving_keys.is_empty() {
1438            return Ok(true);
1439        }
1440
1441        // Use shorter wait timeout in simtests to exercise server-side error paths and
1442        // client-side retry logic.
1443        let max_wait = if cfg!(msim) {
1444            Duration::from_millis(200)
1445        } else {
1446            WAIT_FOR_FASTPATH_INPUT_TIMEOUT
1447        };
1448
1449        match timeout(
1450            max_wait,
1451            self.get_object_cache_reader().notify_read_input_objects(
1452                &fastpath_dependency_objects,
1453                &receiving_keys,
1454                epoch,
1455            ),
1456        )
1457        .await
1458        {
1459            Ok(()) => Ok(true),
1460            // Maybe return an error for unavailable input objects,
1461            // and allow the caller to skip the rest of input checks?
1462            Err(_) => Ok(false),
1463        }
1464    }
1465
1466    /// Returns Some(inputKey) if the object reference should be waited on until it is
1467    /// finalized, before proceeding to input checks.
1468    ///
1469    /// Incorrect decisions here should only affect user experience, not safety:
1470    /// - Waiting unnecessarily adds latency to transaction signing and submission.
1471    /// - Not waiting when needed may cause the transaction to be rejected because input object is unavailable.
1472    fn should_wait_for_dependency_object(&self, obj_ref: ObjectRef) -> Option<InputKey> {
1473        let (obj_id, cur_version, _digest) = obj_ref;
1474        let Some(latest_obj_ref) = self
1475            .get_object_cache_reader()
1476            .get_latest_object_ref_or_tombstone(obj_id)
1477        else {
1478            // Object might not have been created.
1479            return Some(InputKey::VersionedObject {
1480                id: FullObjectID::new(obj_id, None),
1481                version: cur_version,
1482            });
1483        };
1484        let latest_digest = latest_obj_ref.2;
1485        if latest_digest == ObjectDigest::OBJECT_DIGEST_DELETED {
1486            // Do not wait for deleted object and rely on input check instead.
1487            return None;
1488        }
1489        let latest_version = latest_obj_ref.1;
1490        if cur_version <= latest_version {
1491            // Do not wait for version that already exists or has been consumed.
1492            // Let the input check to handle them and return the proper error.
1493            return None;
1494        }
1495        // Wait for the object version to become available.
1496        Some(InputKey::VersionedObject {
1497            id: FullObjectID::new(obj_id, None),
1498            version: cur_version,
1499        })
1500    }
1501
1502    /// Wait for a transaction to be executed. Transactions need to be sequenced by consensus
1503    /// before being enqueued for execution.
1504    ///
1505    /// Only use this in tests.
1506    #[instrument(level = "trace", skip_all)]
1507    pub async fn wait_for_transaction_execution_for_testing(
1508        &self,
1509        transaction: &VerifiedExecutableTransaction,
1510    ) -> TransactionEffects {
1511        self.notify_read_effects_for_testing(
1512            "AuthorityState::wait_for_transaction_execution_for_testing",
1513            *transaction.digest(),
1514        )
1515        .await
1516    }
1517
1518    /// Internal logic to execute a certificate.
1519    ///
1520    /// Guarantees that
1521    /// - If input objects are available, return no permanent failure.
1522    /// - Execution and output commit are atomic. i.e. outputs are only written to storage,
1523    /// on successful execution; crashed execution has no observable effect and can be retried.
1524    ///
1525    /// It is caller's responsibility to ensure input objects are available and locks are set.
1526    /// If this cannot be satisfied by the caller, execute_certificate() should be called instead.
1527    ///
1528    /// Should only be called within sui-core.
1529    #[instrument(level = "trace", skip_all)]
1530    pub fn try_execute_immediately(
1531        &self,
1532        certificate: &VerifiedExecutableTransaction,
1533        mut execution_env: ExecutionEnv,
1534        epoch_store: &Arc<AuthorityPerEpochStore>,
1535    ) -> ExecutionOutput<(TransactionEffects, Option<ExecutionError>)> {
1536        let _scope = monitored_scope("Execution::try_execute_immediately");
1537        let _metrics_guard = self.metrics.internal_execution_latency.start_timer();
1538
1539        let tx_digest = certificate.digest();
1540
1541        if let Some(fork_recovery) = &self.fork_recovery_state
1542            && let Some(override_digest) = fork_recovery.get_transaction_override(tx_digest)
1543        {
1544            warn!(
1545                ?tx_digest,
1546                original_digest = ?execution_env.expected_effects_digest,
1547                override_digest = ?override_digest,
1548                "Applying fork recovery override for transaction effects digest"
1549            );
1550            execution_env.expected_effects_digest =
1551                Some(ExpectedEffectsDigest::Uncertified(override_digest));
1552        }
1553
1554        // prevent concurrent executions of the same tx.
1555        let tx_guard = epoch_store.acquire_tx_guard(certificate);
1556
1557        let tx_cache_reader = self.get_transaction_cache_reader();
1558        if let Some(effects) = tx_cache_reader.get_executed_effects(tx_digest) {
1559            if let Some(expected_effects_digest) = execution_env.expected_effects_digest {
1560                assert_eq!(
1561                    effects.digest(),
1562                    expected_effects_digest.digest(),
1563                    "Unexpected effects digest for transaction {:?}",
1564                    tx_digest
1565                );
1566            }
1567            tx_guard.release();
1568            return ExecutionOutput::Success((effects, None));
1569        }
1570
1571        let execution_start_time = Instant::now();
1572
1573        // Any caller that verifies the signatures on the certificate will have already checked the
1574        // epoch. But paths that don't verify sigs (e.g. execution from checkpoint, reading from db)
1575        // present the possibility of an epoch mismatch. If this cert is not finalzied in previous
1576        // epoch, then it's invalid.
1577        let Some(execution_guard) = self.execution_lock_for_executable_transaction(certificate)
1578        else {
1579            tx_guard.release();
1580            return ExecutionOutput::EpochEnded;
1581        };
1582        // Since we obtain a reference to the epoch store before taking the execution lock, it's
1583        // possible that reconfiguration has happened and they no longer match.
1584        // TODO: We may not need the following check anymore since the scheduler
1585        // should have checked that the certificate is from the same epoch as epoch_store.
1586        if *execution_guard != epoch_store.epoch() {
1587            tx_guard.release();
1588            info!("The epoch of the execution_guard doesn't match the epoch store");
1589            return ExecutionOutput::EpochEnded;
1590        }
1591
1592        let accumulator_version = execution_env.assigned_versions.accumulator_version();
1593
1594        let (transaction_outputs, timings, execution_error_opt) = match self.process_certificate(
1595            &tx_guard,
1596            &execution_guard,
1597            certificate,
1598            execution_env,
1599            epoch_store,
1600        ) {
1601            ExecutionOutput::Success(result) => result,
1602            output => return output.unwrap_err(),
1603        };
1604        let transaction_outputs = Arc::new(transaction_outputs);
1605
1606        fail_point!("crash");
1607
1608        let effects = transaction_outputs.effects.clone();
1609        debug!(
1610            ?tx_digest,
1611            fx_digest=?effects.digest(),
1612            "process_certificate succeeded in {:.3}ms",
1613            (execution_start_time.elapsed().as_micros() as f64) / 1000.0
1614        );
1615
1616        let commit_result = self.commit_certificate(certificate, transaction_outputs, epoch_store);
1617        if let Err(err) = commit_result {
1618            error!(?tx_digest, "Error committing transaction: {err}");
1619            tx_guard.release();
1620            return ExecutionOutput::Fatal(err);
1621        }
1622
1623        if let TransactionKind::AuthenticatorStateUpdate(auth_state) =
1624            certificate.data().transaction_data().kind()
1625        {
1626            if let Some(err) = &execution_error_opt {
1627                debug_fatal!("Authenticator state update failed: {:?}", err);
1628            }
1629            epoch_store.update_authenticator_state(auth_state);
1630
1631            // double check that the signature verifier always matches the authenticator state
1632            if cfg!(debug_assertions) {
1633                let authenticator_state = get_authenticator_state(self.get_object_store())
1634                    .expect("Read cannot fail")
1635                    .expect("Authenticator state must exist");
1636
1637                let mut sys_jwks: Vec<_> = authenticator_state
1638                    .active_jwks
1639                    .into_iter()
1640                    .map(|jwk| (jwk.jwk_id, jwk.jwk))
1641                    .collect();
1642                let mut active_jwks: Vec<_> = epoch_store
1643                    .signature_verifier
1644                    .get_jwks()
1645                    .into_iter()
1646                    .collect();
1647                sys_jwks.sort();
1648                active_jwks.sort();
1649
1650                assert_eq!(sys_jwks, active_jwks);
1651            }
1652        }
1653
1654        if certificate
1655            .transaction_data()
1656            .kind()
1657            .is_accumulator_barrier_settle_tx()
1658        {
1659            let object_funds_checker = self.object_funds_checker.load();
1660            if let Some(object_funds_checker) = object_funds_checker.as_ref() {
1661                // unwrap safe because we assign accumulator version for every transaction
1662                // when accumulator is enabled.
1663                let next_accumulator_version = accumulator_version.unwrap().next();
1664                object_funds_checker.settle_accumulator_version(next_accumulator_version);
1665            }
1666        }
1667
1668        tx_guard.commit_tx();
1669
1670        let elapsed = execution_start_time.elapsed();
1671        epoch_store.record_local_execution_time(
1672            certificate.data().transaction_data(),
1673            &effects,
1674            timings,
1675            elapsed,
1676        );
1677
1678        let elapsed_us = elapsed.as_micros() as f64;
1679        if elapsed_us > 0.0 {
1680            self.metrics
1681                .execution_gas_latency_ratio
1682                .observe(effects.gas_cost_summary().computation_cost as f64 / elapsed_us);
1683        };
1684        ExecutionOutput::Success((effects, execution_error_opt))
1685    }
1686
1687    pub fn read_objects_for_execution(
1688        &self,
1689        tx_lock: &CertLockGuard,
1690        certificate: &VerifiedExecutableTransaction,
1691        assigned_shared_object_versions: &AssignedVersions,
1692        epoch_store: &Arc<AuthorityPerEpochStore>,
1693    ) -> SuiResult<InputObjects> {
1694        let _scope = monitored_scope("Execution::load_input_objects");
1695        let _metrics_guard = self
1696            .metrics
1697            .execution_load_input_objects_latency
1698            .start_timer();
1699        let input_objects = &certificate.data().transaction_data().input_objects()?;
1700        self.input_loader.read_objects_for_execution(
1701            &certificate.key(),
1702            tx_lock,
1703            input_objects,
1704            assigned_shared_object_versions,
1705            epoch_store.epoch(),
1706        )
1707    }
1708
1709    pub async fn try_execute_executable_for_test(
1710        &self,
1711        executable: &VerifiedExecutableTransaction,
1712        execution_env: ExecutionEnv,
1713    ) -> (VerifiedSignedTransactionEffects, Option<ExecutionError>) {
1714        let epoch_store = self.epoch_store_for_testing();
1715        let (effects, execution_error_opt) = self
1716            .try_execute_immediately(executable, execution_env, &epoch_store)
1717            .unwrap();
1718        self.flush_post_processing(executable.digest()).await;
1719        let signed_effects = self.sign_effects(effects, &epoch_store).unwrap();
1720        (signed_effects, execution_error_opt)
1721    }
1722
1723    /// Wait until the effects of the given transaction are available and return them.
1724    /// Panics if the effects are not found.
1725    ///
1726    /// Only use this in tests where effects are expected to exist.
1727    pub async fn notify_read_effects_for_testing(
1728        &self,
1729        task_name: &'static str,
1730        digest: TransactionDigest,
1731    ) -> TransactionEffects {
1732        self.get_transaction_cache_reader()
1733            .notify_read_executed_effects(task_name, &[digest])
1734            .await
1735            .pop()
1736            .expect("must return correct number of effects")
1737    }
1738
1739    /// This function captures the required state to debug a forked transaction.
1740    /// The dump is written to a file in dir `path`, with name prefixed by the transaction digest.
1741    /// NOTE: Since this info escapes the validator context,
1742    /// make sure not to leak any private info here
1743    pub(crate) fn debug_dump_transaction_state(
1744        &self,
1745        tx_digest: &TransactionDigest,
1746        effects: &TransactionEffects,
1747        expected_effects_digest: TransactionEffectsDigest,
1748        inner_temporary_store: &InnerTemporaryStore,
1749        certificate: &VerifiedExecutableTransaction,
1750        debug_dump_config: &StateDebugDumpConfig,
1751    ) -> SuiResult<PathBuf> {
1752        let dump_dir = debug_dump_config
1753            .dump_file_directory
1754            .as_ref()
1755            .cloned()
1756            .unwrap_or(std::env::temp_dir());
1757        let epoch_store = self.load_epoch_store_one_call_per_task();
1758
1759        NodeStateDump::new(
1760            tx_digest,
1761            effects,
1762            expected_effects_digest,
1763            self.get_object_store().as_ref(),
1764            &epoch_store,
1765            inner_temporary_store,
1766            certificate,
1767        )?
1768        .write_to_file(&dump_dir)
1769        .map_err(|e| SuiErrorKind::FileIOError(e.to_string()).into())
1770    }
1771
1772    #[instrument(level = "trace", skip_all)]
1773    pub(crate) fn process_certificate(
1774        &self,
1775        tx_guard: &CertTxGuard,
1776        execution_guard: &ExecutionLockReadGuard<'_>,
1777        certificate: &VerifiedExecutableTransaction,
1778        execution_env: ExecutionEnv,
1779        epoch_store: &Arc<AuthorityPerEpochStore>,
1780    ) -> ExecutionOutput<(
1781        TransactionOutputs,
1782        Vec<ExecutionTiming>,
1783        Option<ExecutionError>,
1784    )> {
1785        let _scope = monitored_scope("Execution::process_certificate");
1786
1787        let input_objects = match self.read_objects_for_execution(
1788            tx_guard.as_lock_guard(),
1789            certificate,
1790            &execution_env.assigned_versions,
1791            epoch_store,
1792        ) {
1793            Ok(objects) => objects,
1794            Err(e) => return ExecutionOutput::Fatal(e),
1795        };
1796
1797        let expected_effects_digest = execution_env.expected_effects_digest;
1798
1799        fail_point_if!("correlated-crash-process-certificate", || {
1800            if sui_simulator::random::deterministic_probability_once(certificate.digest(), 0.01) {
1801                sui_simulator::task::kill_current_node(None);
1802            }
1803        });
1804
1805        // Errors originating from prepare_certificate may be transient (failure to read locks) or
1806        // non-transient (transaction input is invalid, move vm errors). However, all errors from
1807        // this function occur before we have written anything to the db, so we commit the tx
1808        // guard and rely on the client to retry the tx (if it was transient).
1809        self.execute_certificate(
1810            execution_guard,
1811            certificate,
1812            input_objects,
1813            expected_effects_digest,
1814            execution_env,
1815            epoch_store,
1816        )
1817    }
1818
1819    pub async fn reconfigure_traffic_control(
1820        &self,
1821        params: TrafficControlReconfigParams,
1822    ) -> Result<TrafficControlReconfigParams, SuiError> {
1823        if let Some(traffic_controller) = self.traffic_controller.as_ref() {
1824            traffic_controller.admin_reconfigure(params).await
1825        } else {
1826            Err(SuiErrorKind::InvalidAdminRequest(
1827                "Traffic controller is not configured on this node".to_string(),
1828            )
1829            .into())
1830        }
1831    }
1832
1833    #[instrument(level = "trace", skip_all)]
1834    fn commit_certificate(
1835        &self,
1836        certificate: &VerifiedExecutableTransaction,
1837        transaction_outputs: Arc<TransactionOutputs>,
1838        epoch_store: &Arc<AuthorityPerEpochStore>,
1839    ) -> SuiResult {
1840        let _scope: Option<mysten_metrics::MonitoredScopeGuard> =
1841            monitored_scope("Execution::commit_certificate");
1842        let _metrics_guard = self.metrics.commit_certificate_latency.start_timer();
1843
1844        let tx_digest = certificate.digest();
1845
1846        // The insertion to epoch_store is not atomic with the insertion to the perpetual store. This is OK because
1847        // we insert to the epoch store first. And during lookups we always look up in the perpetual store first.
1848        epoch_store.insert_executed_in_epoch(tx_digest);
1849        let key = certificate.key();
1850        if !matches!(key, TransactionKey::Digest(_)) {
1851            epoch_store.insert_tx_key(key, *tx_digest)?;
1852        }
1853
1854        // Allow testing what happens if we crash here.
1855        fail_point!("crash");
1856
1857        self.get_cache_writer()
1858            .write_transaction_outputs(epoch_store.epoch(), transaction_outputs);
1859
1860        // Fire an in-memory-only notification so the scheduler can detect that this
1861        // barrier transaction has been executed (e.g. by the checkpoint executor) and
1862        // stop waiting for the checkpoint builder.  We intentionally do NOT persist
1863        // this to the DB to avoid stale entries surviving a crash while effects may not.
1864        if let Some(settlement_key) = certificate
1865            .transaction_data()
1866            .kind()
1867            .accumulator_barrier_settlement_key()
1868        {
1869            epoch_store.notify_barrier_executed(settlement_key, *tx_digest);
1870        }
1871
1872        if certificate.transaction_data().is_end_of_epoch_tx() {
1873            // At the end of epoch, since system packages may have been upgraded, force
1874            // reload them in the cache.
1875            self.get_object_cache_reader()
1876                .force_reload_system_packages(&BuiltInFramework::all_package_ids());
1877        }
1878
1879        Ok(())
1880    }
1881
1882    fn update_metrics(
1883        &self,
1884        certificate: &VerifiedExecutableTransaction,
1885        inner_temporary_store: &InnerTemporaryStore,
1886        effects: &TransactionEffects,
1887    ) {
1888        // count signature by scheme, for zklogin and multisig
1889        if certificate.has_zklogin_sig() {
1890            self.metrics.zklogin_sig_count.inc();
1891        } else if certificate.has_upgraded_multisig() {
1892            self.metrics.multisig_sig_count.inc();
1893        }
1894
1895        self.metrics.total_effects.inc();
1896        self.metrics.total_certs.inc();
1897
1898        let consensus_object_count = effects.input_consensus_objects().len();
1899        if consensus_object_count > 0 {
1900            self.metrics.shared_obj_tx.inc();
1901        }
1902
1903        if certificate.is_sponsored_tx() {
1904            self.metrics.sponsored_tx.inc();
1905        }
1906
1907        let input_object_count = inner_temporary_store.input_objects.len();
1908        self.metrics
1909            .num_input_objs
1910            .observe(input_object_count as f64);
1911        self.metrics
1912            .num_shared_objects
1913            .observe(consensus_object_count as f64);
1914        self.metrics.batch_size.observe(
1915            certificate
1916                .data()
1917                .intent_message()
1918                .value
1919                .kind()
1920                .num_commands() as f64,
1921        );
1922    }
1923
1924    /// Runs the executor on an already-prepared transaction; the caller is responsible for any
1925    /// coin reservation rewriting.
1926    fn execute_transaction_to_effects(
1927        &self,
1928        executor: &dyn Executor,
1929        store: &dyn BackingStore,
1930        protocol_config: &ProtocolConfig,
1931        enable_expensive_checks: bool,
1932        execution_params: ExecutionOrEarlyError,
1933        epoch_id: &EpochId,
1934        epoch_timestamp_ms: u64,
1935        input_objects: CheckedInputObjects,
1936        system_object_versions: BTreeMap<ObjectID, SequenceNumber>,
1937        gas_data: GasData,
1938        gas_status: SuiGasStatus,
1939        kind: TransactionKind,
1940        rewritten_inputs: Option<Vec<bool>>,
1941        signer: SuiAddress,
1942        tx_digest: TransactionDigest,
1943    ) -> (
1944        InnerTemporaryStore,
1945        SuiGasStatus,
1946        TransactionEffects,
1947        Vec<ExecutionTiming>,
1948        Result<(), ExecutionError>,
1949    ) {
1950        let (inner_temp_store, gas_status, effects, timings, execution_error) = executor
1951            // TODO only run this function on FullNodes, use `execute_transaction_to_effects` on validators.
1952            .execute_transaction_to_effects_and_execution_error(
1953                store,
1954                protocol_config,
1955                self.metrics.execution_metrics.clone(),
1956                enable_expensive_checks,
1957                execution_params,
1958                epoch_id,
1959                epoch_timestamp_ms,
1960                input_objects,
1961                system_object_versions,
1962                gas_data,
1963                gas_status,
1964                kind,
1965                rewritten_inputs,
1966                signer,
1967                tx_digest,
1968                &mut None,
1969            );
1970
1971        (
1972            inner_temp_store,
1973            gas_status,
1974            effects,
1975            timings,
1976            execution_error,
1977        )
1978    }
1979
1980    /// execute_certificate validates the transaction input, and executes the certificate,
1981    /// returning transaction outputs.
1982    ///
1983    /// It reads state from the db (both owned and shared locks), but it has no side effects.
1984    ///
1985    /// Executes a certificate and returns an ExecutionOutput.
1986    /// The function can fail with Fatal errors (e.g., the transaction input is invalid,
1987    /// locks are not held correctly, etc.) or transient errors (e.g., db read errors).
1988    #[instrument(level = "trace", skip_all)]
1989    fn execute_certificate(
1990        &self,
1991        _execution_guard: &ExecutionLockReadGuard<'_>,
1992        certificate: &VerifiedExecutableTransaction,
1993        input_objects: InputObjects,
1994        expected_effects_digest: Option<ExpectedEffectsDigest>,
1995        execution_env: ExecutionEnv,
1996        epoch_store: &Arc<AuthorityPerEpochStore>,
1997    ) -> ExecutionOutput<(
1998        TransactionOutputs,
1999        Vec<ExecutionTiming>,
2000        Option<ExecutionError>,
2001    )> {
2002        let _scope = monitored_scope("Execution::prepare_certificate");
2003        let _metrics_guard = self.metrics.prepare_certificate_latency.start_timer();
2004        let prepare_certificate_start_time = tokio::time::Instant::now();
2005
2006        // TODO: We need to move this to a more appropriate place to avoid redundant checks.
2007        let tx_data = certificate.data().transaction_data();
2008
2009        if let Err(e) = tx_data.validity_check(&epoch_store.tx_validity_check_context()) {
2010            return ExecutionOutput::Fatal(e);
2011        }
2012
2013        // The cost of partially re-auditing a transaction before execution is tolerated.
2014        // This step is required for correctness because, for example, ConsensusAddressOwner
2015        // object owner may have changed between signing and execution.
2016        let (gas_status, input_objects) = match sui_transaction_checks::check_certificate_input(
2017            certificate,
2018            input_objects,
2019            epoch_store.protocol_config(),
2020            epoch_store.reference_gas_price(),
2021        ) {
2022            Ok(result) => result,
2023            Err(e) => return ExecutionOutput::Fatal(e),
2024        };
2025
2026        let tx_digest = *certificate.digest();
2027        let protocol_config = epoch_store.protocol_config();
2028        let transaction_data = &certificate.data().intent_message().value;
2029        let sender = transaction_data.sender();
2030        let (mut kind, signer, gas_data) = transaction_data.execution_parts();
2031        let early_execution_error = get_early_execution_error(
2032            &tx_digest,
2033            &input_objects,
2034            self.config.certificate_deny_config.certificate_deny_set(),
2035            &execution_env.funds_withdraw_status,
2036        );
2037        // Versions of system objects this transaction may read during execution, each at the version
2038        // it was sequenced against.
2039        let system_object_versions = execution_env
2040            .assigned_versions
2041            .system_object_versions
2042            .clone();
2043        let accumulator_version = execution_env.assigned_versions.accumulator_version();
2044        let execution_params = match early_execution_error {
2045            None => ExecutionOrEarlyError::ok(accumulator_version),
2046            Some(errors) => ExecutionOrEarlyError::failed(errors, accumulator_version),
2047        };
2048
2049        // Skip on early error: the tx will fail anyway and rewriting may fail if the accumulator
2050        // was deleted.
2051        let rewritten_inputs = if execution_params.is_ok() {
2052            rewrite_transaction_for_coin_reservations(
2053                self.chain_identifier,
2054                &*self.coin_reservation_resolver,
2055                sender,
2056                &mut kind,
2057                execution_env.assigned_versions.accumulator_version(),
2058            )
2059            .expect("rewriting must succeed for a certified transaction")
2060        } else {
2061            None
2062        };
2063
2064        let tracking_store = TrackingBackingStore::new(self.get_backing_store().as_ref());
2065
2066        #[allow(unused_mut)]
2067        let (inner_temp_store, _, mut effects, timings, execution_error_opt) = self
2068            .execute_transaction_to_effects(
2069                &**epoch_store.executor(),
2070                &tracking_store,
2071                protocol_config,
2072                // TODO: would be nice to pass the whole NodeConfig here, but it creates a
2073                // cyclic dependency w/ sui-adapter
2074                self.config
2075                    .expensive_safety_check_config
2076                    .enable_deep_per_tx_sui_conservation_check(),
2077                execution_params,
2078                &epoch_store.epoch_start_config().epoch_data().epoch_id(),
2079                epoch_store
2080                    .epoch_start_config()
2081                    .epoch_data()
2082                    .epoch_start_timestamp(),
2083                input_objects,
2084                system_object_versions,
2085                gas_data,
2086                gas_status,
2087                kind,
2088                rewritten_inputs,
2089                signer,
2090                tx_digest,
2091            );
2092
2093        let object_funds_checker = self.object_funds_checker.load();
2094        if let Some(object_funds_checker) = object_funds_checker.as_ref()
2095            && !object_funds_checker.should_commit_object_funds_withdraws(
2096                certificate,
2097                &effects,
2098                &inner_temp_store.accumulator_running_max_withdraws,
2099                &execution_env,
2100                self.get_account_funds_read(),
2101                &self.execution_scheduler,
2102                epoch_store,
2103            )
2104        {
2105            assert_reachable!("retry object withdraw later");
2106            return ExecutionOutput::RetryLater;
2107        }
2108
2109        // (test-only) Inject a fork before the effects-digest check below. Placed here so that a
2110        // forked validator executing a *certified* checkpoint (expected_effects_digest is set, i.e.
2111        // the checkpoint-executor path) trips the transaction-fork check. On the builder path
2112        // (expected_effects_digest is None) the check is skipped, so this still produces a checkpoint
2113        // fork as before.
2114        fail_point_arg!("simulate_fork_during_execution", |(
2115            forked_validators,
2116            full_halt,
2117            effects_overrides,
2118            fork_probability,
2119            executor_path_only,
2120        ): (
2121            std::sync::Arc<
2122                std::sync::Mutex<std::collections::HashSet<sui_types::base_types::AuthorityName>>,
2123            >,
2124            bool,
2125            std::sync::Arc<std::sync::Mutex<std::collections::BTreeMap<String, String>>>,
2126            f32,
2127            bool,
2128        )| {
2129            #[cfg(msim)]
2130            // When `executor_path_only` is set, fork only while executing a certified checkpoint
2131            // (expected_effects_digest is set) so the divergence trips the transaction-fork check
2132            // below; otherwise fork on any path (the builder path yields a checkpoint fork).
2133            if !executor_path_only || expected_effects_digest.is_some() {
2134                self.simulate_fork_during_execution(
2135                    certificate,
2136                    epoch_store,
2137                    &mut effects,
2138                    forked_validators,
2139                    full_halt,
2140                    effects_overrides,
2141                    fork_probability,
2142                );
2143            }
2144        });
2145
2146        if let Some(expected) = expected_effects_digest
2147            && effects.digest() != expected.digest()
2148        {
2149            let expected_effects_digest = expected.digest();
2150            // We dont want to mask the original error, so we log it and continue.
2151            match self.debug_dump_transaction_state(
2152                &tx_digest,
2153                &effects,
2154                expected_effects_digest,
2155                &inner_temp_store,
2156                certificate,
2157                &self.config.state_debug_dump_config,
2158            ) {
2159                Ok(out_path) => {
2160                    info!(
2161                        "Dumped node state for transaction {} to {}",
2162                        tx_digest,
2163                        out_path.as_path().display().to_string()
2164                    );
2165                }
2166                Err(e) => {
2167                    error!("Error dumping state for transaction {}: {e}", tx_digest);
2168                }
2169            }
2170            let expected_effects = self
2171                .get_transaction_cache_reader()
2172                .get_effects(&expected_effects_digest);
2173            error!(
2174                ?tx_digest,
2175                ?expected,
2176                actual_effects = ?effects,
2177                expected_effects = ?expected_effects,
2178                "fork detected!"
2179            );
2180            if let Err(e) = self.checkpoint_store.record_transaction_fork_detected(
2181                tx_digest,
2182                expected_effects_digest,
2183                effects.digest(),
2184                expected.checkpoint_seq(),
2185            ) {
2186                error!("Failed to record transaction fork: {e}");
2187            }
2188
2189            fail_point_if!("kill_transaction_fork_node", || {
2190                #[cfg(msim)]
2191                {
2192                    tracing::error!(
2193                        fatal = true,
2194                        "Fork recovery test: killing node due to transaction effects fork for digest: {}",
2195                        tx_digest
2196                    );
2197                    sui_simulator::task::shutdown_current_node();
2198                }
2199            });
2200
2201            fatal!(
2202                "Transaction {} is expected to have effects digest {}, but got {}!",
2203                tx_digest,
2204                expected_effects_digest,
2205                effects.digest()
2206            );
2207        }
2208
2209        let unchanged_loaded_runtime_objects =
2210            crate::transaction_outputs::unchanged_loaded_runtime_objects(
2211                certificate.transaction_data(),
2212                &effects,
2213                &tracking_store.into_read_objects(),
2214            );
2215
2216        // index certificate
2217        let _ = self
2218            .post_process_one_tx(certificate, &effects, &inner_temp_store, epoch_store)
2219            .tap_err(|e| {
2220                self.metrics.post_processing_total_failures.inc();
2221                error!(?tx_digest, "tx post processing failed: {e}");
2222            });
2223
2224        self.update_metrics(certificate, &inner_temp_store, &effects);
2225
2226        let transaction_outputs = TransactionOutputs::build_transaction_outputs(
2227            certificate.clone().into_unsigned(),
2228            effects,
2229            inner_temp_store,
2230            unchanged_loaded_runtime_objects,
2231        );
2232
2233        let elapsed = prepare_certificate_start_time.elapsed().as_micros() as f64;
2234        if elapsed > 0.0 {
2235            self.metrics.prepare_cert_gas_latency_ratio.observe(
2236                transaction_outputs
2237                    .effects
2238                    .gas_cost_summary()
2239                    .computation_cost as f64
2240                    / elapsed,
2241            );
2242        }
2243
2244        ExecutionOutput::Success((transaction_outputs, timings, execution_error_opt.err()))
2245    }
2246
2247    pub fn prepare_certificate_for_benchmark(
2248        &self,
2249        certificate: &VerifiedExecutableTransaction,
2250        input_objects: InputObjects,
2251        epoch_store: &Arc<AuthorityPerEpochStore>,
2252    ) -> SuiResult<(TransactionOutputs, Option<ExecutionError>)> {
2253        let lock = RwLock::new(epoch_store.epoch());
2254        let execution_guard = lock.try_read().unwrap();
2255
2256        let (transaction_outputs, _timings, execution_error_opt) = self
2257            .execute_certificate(
2258                &execution_guard,
2259                certificate,
2260                input_objects,
2261                None,
2262                ExecutionEnv::default(),
2263                epoch_store,
2264            )
2265            .unwrap();
2266        Ok((transaction_outputs, execution_error_opt))
2267    }
2268
2269    #[instrument(skip_all)]
2270    #[allow(clippy::type_complexity)]
2271    pub async fn dry_exec_transaction(
2272        &self,
2273        transaction: TransactionData,
2274    ) -> SuiResult<(
2275        DryRunTransactionBlockResponse,
2276        BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
2277        TransactionEffects,
2278        Option<ObjectID>,
2279    )> {
2280        let epoch_store = self.load_epoch_store_one_call_per_task();
2281        if !self.is_fullnode(&epoch_store) {
2282            return Err(SuiErrorKind::UnsupportedFeatureError {
2283                error: "dry-exec is only supported on fullnodes".to_string(),
2284            }
2285            .into());
2286        }
2287
2288        if transaction.kind().is_system_tx() {
2289            return Err(SuiErrorKind::UnsupportedFeatureError {
2290                error: "dry-exec does not support system transactions".to_string(),
2291            }
2292            .into());
2293        }
2294
2295        self.dry_exec_transaction_impl(&epoch_store, transaction)
2296    }
2297
2298    #[allow(clippy::type_complexity)]
2299    fn dry_exec_transaction_impl(
2300        &self,
2301        epoch_store: &AuthorityPerEpochStore,
2302        transaction: TransactionData,
2303    ) -> SuiResult<(
2304        DryRunTransactionBlockResponse,
2305        BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
2306        TransactionEffects,
2307        Option<ObjectID>,
2308    )> {
2309        // Route through `simulate_transaction` -- `dry-exec` matches
2310        // `simulate_transaction(_, TransactionChecks::Enabled, _)`. The deny-config
2311        // check runs inside `simulate_transaction` via `pre_object_load_checks`, so we
2312        // don't need to invoke it directly here.
2313        let sim = self.simulate_transaction(
2314            transaction.clone(),
2315            TransactionChecks::Enabled,
2316            /* allow_mock_gas_coin */ true,
2317        )?;
2318
2319        self.build_dry_run_response(epoch_store, transaction, sim)
2320    }
2321
2322    /// Adapt a `SimulateTransactionResult` into the
2323    /// `(DryRunTransactionBlockResponse, written_objects_with_kind, effects, mock_gas_id)`
2324    /// tuple that the JSON-RPC dry-run layer consumes. Shared between
2325    /// `dry_exec_transaction_impl` and `dry_exec_transaction_for_benchmark`'s
2326    /// callers; pulls together:
2327    ///   - layout resolution over the simulate-produced `ObjectSet`,
2328    ///   - `(ObjectRef, Object, WriteKind)` derivation by walking
2329    ///     `effects.created / unwrapped / mutated` against that `ObjectSet`,
2330    ///   - `execution_error_source` from `sim.execution_result`,
2331    ///   - the `SuiTransactionBlockData` / `SuiTransactionBlockEffects` /
2332    ///     `SuiTransactionBlockEvents` conversions.
2333    #[allow(clippy::type_complexity)]
2334    fn build_dry_run_response(
2335        &self,
2336        epoch_store: &AuthorityPerEpochStore,
2337        transaction: TransactionData,
2338        sim: SimulateTransactionResult,
2339    ) -> SuiResult<(
2340        DryRunTransactionBlockResponse,
2341        BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
2342        TransactionEffects,
2343        Option<ObjectID>,
2344    )> {
2345        let SimulateTransactionResult {
2346            effects,
2347            events,
2348            objects,
2349            execution_result,
2350            mock_gas_id,
2351            suggested_gas_price,
2352            ..
2353        } = sim;
2354
2355        let tx_digest = *effects.transaction_digest();
2356
2357        // Walk effects' created / unwrapped / mutated lists against the
2358        // simulate-produced ObjectSet (which carries both input and written
2359        // objects). Refs missing from `objects` are dropped silently — that
2360        // would indicate a simulate-vs-effects inconsistency.
2361        let written_with_kind: BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)> = effects
2362            .created()
2363            .into_iter()
2364            .map(|(oref, _)| (oref, WriteKind::Create))
2365            .chain(
2366                effects
2367                    .unwrapped()
2368                    .into_iter()
2369                    .map(|(oref, _)| (oref, WriteKind::Unwrap)),
2370            )
2371            .chain(
2372                effects
2373                    .mutated()
2374                    .into_iter()
2375                    .map(|(oref, _)| (oref, WriteKind::Mutate)),
2376            )
2377            .filter_map(|(oref, kind)| {
2378                objects
2379                    .get(&ObjectKey(oref.0, oref.1))
2380                    .map(|obj| (oref.0, (oref, obj.clone(), kind)))
2381            })
2382            .collect();
2383
2384        // Resolve event/object layouts against the simulate-produced ObjectSet
2385        // (newly-published packages from the simulation appear there), with the
2386        // node's backing package store as fallback for already-on-chain packages.
2387        let mut layout_resolver = epoch_store.executor().type_layout_resolver(
2388            epoch_store.protocol_config(),
2389            Box::new(OverlayBackingPackageStore::new(
2390                &objects,
2391                self.get_backing_package_store(),
2392            )),
2393        );
2394
2395        let execution_error_source = execution_result
2396            .as_ref()
2397            .err()
2398            .and_then(|e| e.source().as_ref().map(|e| e.to_string()));
2399
2400        let response = DryRunTransactionBlockResponse {
2401            suggested_gas_price,
2402            input: SuiTransactionBlockData::try_from_with_module_cache(
2403                transaction,
2404                &epoch_store.module_cache().clone(),
2405            )
2406            .map_err(|e| SuiErrorKind::TransactionSerializationError {
2407                error: format!("Failed to convert transaction to SuiTransactionBlockData: {e}"),
2408            })?,
2409            effects: effects.clone().try_into()?,
2410            events: SuiTransactionBlockEvents::try_from(
2411                events.unwrap_or_default(),
2412                tx_digest,
2413                None,
2414                layout_resolver.as_mut(),
2415            )?,
2416            // The RPC layer recalculates object_changes / balance_changes from
2417            // the written_objects map and effects.
2418            object_changes: Vec::new(),
2419            balance_changes: Vec::new(),
2420            execution_error_source,
2421        };
2422
2423        Ok((response, written_with_kind, effects, mock_gas_id))
2424    }
2425
2426    pub fn simulate_transaction(
2427        &self,
2428        mut transaction: TransactionData,
2429        checks: TransactionChecks,
2430        allow_mock_gas_coin: bool,
2431    ) -> SuiResult<SimulateTransactionResult> {
2432        if transaction.kind().is_system_tx() {
2433            return Err(SuiErrorKind::UnsupportedFeatureError {
2434                error: "simulate does not support system transactions".to_string(),
2435            }
2436            .into());
2437        }
2438
2439        let epoch_store = self.load_epoch_store_one_call_per_task();
2440        if !self.is_fullnode(&epoch_store) {
2441            return Err(SuiErrorKind::UnsupportedFeatureError {
2442                error: "simulate is only supported on fullnodes".to_string(),
2443            }
2444            .into());
2445        }
2446
2447        let dev_inspect = checks.disabled();
2448        if dev_inspect && self.config.dev_inspect_disabled {
2449            return Err(SuiErrorKind::UnsupportedFeatureError {
2450                error: "simulate with checks disabled is not allowed on this node".to_string(),
2451            }
2452            .into());
2453        }
2454
2455        // Reject coin reservations in gas payment when the execution engine
2456        // doesn't support them.
2457        let protocol_config = epoch_store.protocol_config();
2458        if !protocol_config.enable_coin_reservation_obj_refs()
2459            && transaction.gas().iter().any(|obj_ref| {
2460                sui_types::coin_reservation::ParsedDigest::is_coin_reservation_digest(&obj_ref.2)
2461            })
2462        {
2463            return Err(SuiErrorKind::UnsupportedFeatureError {
2464                error:
2465                    "coin reservations in gas payment are not supported at this protocol version"
2466                        .to_string(),
2467            }
2468            .into());
2469        }
2470
2471        // Compute input/receiving object kinds before mock gas injection so the mock
2472        // gas reference is not included in input_object_kinds (it is added to
2473        // input_objects directly after object loading).
2474        let input_object_kinds = transaction.input_objects()?;
2475        let receiving_object_refs = transaction.receiving_objects();
2476
2477        // Inject mock gas coin before validity_check so that on protocol versions
2478        // where address-balance gas payments are not yet enabled, the non-empty
2479        // payment check in validity_check passes for simulate/dev-inspect requests
2480        // submitted without explicit gas.
2481        // Also required before pre_object_load_checks so that funds-withdrawal
2482        // processing sees non-empty payment and doesn't create an address-balance
2483        // withdrawal for gas.
2484        // Skip mock gas for gasless transactions — they don't use gas coins.
2485        let is_gasless = protocol_config.enable_gasless() && transaction.is_gasless_transaction();
2486        let mock_gas_object = if allow_mock_gas_coin && transaction.gas().is_empty() && !is_gasless
2487        {
2488            let obj = Object::new_move(
2489                MoveObject::new_gas_coin(
2490                    OBJECT_START_VERSION,
2491                    ObjectID::MAX,
2492                    DEV_INSPECT_GAS_COIN_VALUE,
2493                ),
2494                Owner::AddressOwner(transaction.gas_data().owner),
2495                TransactionDigest::genesis_marker(),
2496            );
2497            transaction.gas_data_mut().payment = vec![obj.compute_object_reference()];
2498            Some(obj)
2499        } else {
2500            None
2501        };
2502
2503        // Full validity check including gas budget and price.
2504        transaction.validity_check(&epoch_store.tx_validity_check_context())?;
2505
2506        let declared_withdrawals = self.pre_object_load_checks(
2507            &transaction,
2508            &[],
2509            &input_object_kinds,
2510            &receiving_object_refs,
2511            epoch_store.protocol_config(),
2512        )?;
2513        let address_funds: BTreeSet<_> = declared_withdrawals.keys().cloned().collect();
2514
2515        let (mut input_objects, receiving_objects) = self.input_loader.read_objects_for_signing(
2516            // We don't want to cache this transaction since it's a simulation.
2517            None,
2518            &input_object_kinds,
2519            &receiving_object_refs,
2520            epoch_store.epoch(),
2521        )?;
2522
2523        // Add mock gas to input objects after loading (it doesn't exist in the store).
2524        let mock_gas_id = mock_gas_object.map(|obj| {
2525            let id = obj.id();
2526            input_objects.push(ObjectReadResult::new_from_gas_object(&obj));
2527            id
2528        });
2529
2530        let protocol_config = epoch_store.protocol_config();
2531
2532        let (gas_status, checked_input_objects) = if dev_inspect {
2533            sui_transaction_checks::check_dev_inspect_input(
2534                protocol_config,
2535                &transaction,
2536                input_objects,
2537                receiving_objects,
2538                epoch_store.reference_gas_price(),
2539            )?
2540        } else {
2541            sui_transaction_checks::check_transaction_input(
2542                epoch_store.protocol_config(),
2543                epoch_store.reference_gas_price(),
2544                &transaction,
2545                input_objects,
2546                &receiving_objects,
2547                &self.metrics.bytecode_verifier_metrics,
2548                &self.config.verifier_signing_config,
2549            )?
2550        };
2551
2552        let executor = epoch_store.simulate_executor();
2553
2554        let (mut kind, signer, gas_data) = transaction.execution_parts();
2555        let rewritten_inputs = rewrite_transaction_for_coin_reservations(
2556            self.chain_identifier,
2557            &*self.coin_reservation_resolver,
2558            signer,
2559            &mut kind,
2560            None,
2561        )?;
2562        let early_execution_error = get_early_execution_error(
2563            &transaction.digest(),
2564            &checked_input_objects,
2565            self.config.certificate_deny_config.certificate_deny_set(),
2566            &FundsWithdrawStatus::MaybeSufficient,
2567        );
2568        // Dev-inspect/simulation path (not committed): no assigned accumulator version here, so the
2569        // IFFW short-circuit applies unconditionally (`None`), matching non-mainnet execution.
2570        let execution_params = match early_execution_error {
2571            None => ExecutionOrEarlyError::ok(None),
2572            Some(errors) => ExecutionOrEarlyError::failed(errors, None),
2573        };
2574
2575        let tracking_store = TrackingBackingStore::new(self.get_backing_store().as_ref());
2576
2577        // Clone inputs for potential retry if object funds check fails post-execution.
2578        let cloned_input_objects = checked_input_objects.clone();
2579        let cloned_gas = gas_data.clone();
2580        let cloned_kind = kind.clone();
2581        let tx_digest = transaction.digest();
2582        let epoch_id = epoch_store.epoch_start_config().epoch_data().epoch_id();
2583        let epoch_timestamp_ms = epoch_store
2584            .epoch_start_config()
2585            .epoch_data()
2586            .epoch_start_timestamp();
2587        let (inner_temp_store, _, effects, execution_result) = executor.dev_inspect_transaction(
2588            &tracking_store,
2589            protocol_config,
2590            self.metrics.execution_metrics.clone(),
2591            false, // expensive_checks
2592            execution_params,
2593            &epoch_id,
2594            epoch_timestamp_ms,
2595            checked_input_objects,
2596            gas_data,
2597            gas_status,
2598            kind,
2599            rewritten_inputs.clone(),
2600            signer,
2601            tx_digest,
2602            dev_inspect,
2603        );
2604
2605        // Post-execution: check object funds (non-address withdrawals discovered during execution).
2606        let (inner_temp_store, effects, execution_result) = if execution_result.is_ok() {
2607            let has_insufficient_object_funds = inner_temp_store
2608                .accumulator_running_max_withdraws
2609                .iter()
2610                .filter(|(id, _)| !address_funds.contains(id))
2611                .any(|(id, max_withdraw)| {
2612                    let balance = self.get_account_funds_read().get_latest_account_amount(id);
2613                    balance < *max_withdraw
2614                });
2615
2616            if has_insufficient_object_funds {
2617                let retry_gas_status = SuiGasStatus::new(
2618                    cloned_gas.budget,
2619                    cloned_gas.price,
2620                    epoch_store.reference_gas_price(),
2621                    protocol_config,
2622                )?;
2623                let (store, _, effects, result) = executor.dev_inspect_transaction(
2624                    &tracking_store,
2625                    protocol_config,
2626                    self.metrics.execution_metrics.clone(),
2627                    false,
2628                    ExecutionOrEarlyError::failed(
2629                        NonEmpty::new(ExecutionErrorKind::InsufficientFundsForWithdraw),
2630                        None,
2631                    ),
2632                    &epoch_id,
2633                    epoch_timestamp_ms,
2634                    cloned_input_objects,
2635                    cloned_gas,
2636                    retry_gas_status,
2637                    cloned_kind,
2638                    rewritten_inputs,
2639                    signer,
2640                    tx_digest,
2641                    dev_inspect,
2642                );
2643                (store, effects, result)
2644            } else {
2645                (inner_temp_store, effects, execution_result)
2646            }
2647        } else {
2648            (inner_temp_store, effects, execution_result)
2649        };
2650
2651        let loaded_runtime_objects = tracking_store.into_read_objects();
2652        let unchanged_loaded_runtime_objects =
2653            crate::transaction_outputs::unchanged_loaded_runtime_objects(
2654                &transaction,
2655                &effects,
2656                &loaded_runtime_objects,
2657            );
2658
2659        let object_set = {
2660            let objects = {
2661                let mut objects = loaded_runtime_objects;
2662
2663                for o in inner_temp_store
2664                    .input_objects
2665                    .into_values()
2666                    .chain(inner_temp_store.written.into_values())
2667                {
2668                    objects.insert(o);
2669                }
2670
2671                objects
2672            };
2673
2674            let object_keys = sui_types::storage::get_transaction_object_set(
2675                &transaction,
2676                &effects,
2677                &unchanged_loaded_runtime_objects,
2678            );
2679
2680            let mut set = sui_types::full_checkpoint_content::ObjectSet::default();
2681            for k in object_keys {
2682                if let Some(o) = objects.get(&k) {
2683                    set.insert(o.clone());
2684                }
2685            }
2686
2687            set
2688        };
2689
2690        Ok(SimulateTransactionResult {
2691            objects: object_set,
2692            events: effects.events_digest().map(|_| inner_temp_store.events),
2693            effects,
2694            execution_result,
2695            mock_gas_id,
2696            unchanged_loaded_runtime_objects,
2697            suggested_gas_price: self
2698                .congestion_tracker
2699                .get_suggested_gas_prices(&transaction),
2700        })
2701    }
2702
2703    /// The object ID for gas can be any object ID, even for an uncreated object
2704    #[instrument(skip_all)]
2705    pub async fn dev_inspect_transaction_block(
2706        &self,
2707        sender: SuiAddress,
2708        transaction_kind: TransactionKind,
2709        gas_price: Option<u64>,
2710        gas_budget: Option<u64>,
2711        gas_sponsor: Option<SuiAddress>,
2712        gas_objects: Option<Vec<ObjectRef>>,
2713        show_raw_txn_data_and_effects: Option<bool>,
2714        skip_checks: Option<bool>,
2715    ) -> SuiResult<DevInspectResults> {
2716        let epoch_store = self.load_epoch_store_one_call_per_task();
2717        let protocol_config = epoch_store.protocol_config();
2718        let reference_gas_price = epoch_store.reference_gas_price();
2719
2720        let skip_checks = skip_checks.unwrap_or(true);
2721        let show_raw_txn_data_and_effects = show_raw_txn_data_and_effects.unwrap_or(false);
2722
2723        // Synthesize the full TransactionData the caller would have signed.
2724        let price = gas_price.unwrap_or(reference_gas_price);
2725        let budget = gas_budget.unwrap_or(protocol_config.max_tx_gas());
2726        let owner = gas_sponsor.unwrap_or(sender);
2727        let payment = gas_objects.unwrap_or_default();
2728        let transaction = TransactionData::V1(TransactionDataV1 {
2729            kind: transaction_kind,
2730            sender,
2731            gas_data: GasData {
2732                payment,
2733                owner,
2734                price,
2735                budget,
2736            },
2737            expiration: TransactionExpiration::None,
2738        });
2739
2740        // Capture raw bytes before simulate (which may mutate gas_data for mock
2741        // gas injection).
2742        let raw_txn_data = if show_raw_txn_data_and_effects {
2743            bcs::to_bytes(&transaction).map_err(|_| {
2744                SuiErrorKind::TransactionSerializationError {
2745                    error: "Failed to serialize transaction during dev inspect".to_string(),
2746                }
2747            })?
2748        } else {
2749            vec![]
2750        };
2751
2752        // Route through `simulate_transaction`:
2753        //   skip_checks = true  → TransactionChecks::Disabled
2754        //   skip_checks = false → TransactionChecks::Enabled
2755        // The deny-config check runs inside `simulate_transaction` via
2756        // `pre_object_load_checks`, so we don't invoke it directly here.
2757        let checks = if skip_checks {
2758            TransactionChecks::Disabled
2759        } else {
2760            TransactionChecks::Enabled
2761        };
2762        let sim =
2763            self.simulate_transaction(transaction, checks, /* allow_mock_gas_coin */ true)?;
2764
2765        let raw_effects = if show_raw_txn_data_and_effects {
2766            bcs::to_bytes(&sim.effects).map_err(|_| {
2767                SuiErrorKind::TransactionSerializationError {
2768                    error: "Failed to serialize transaction effects during dev inspect".to_string(),
2769                }
2770            })?
2771        } else {
2772            vec![]
2773        };
2774
2775        // Resolve event/object layouts against the simulate-produced ObjectSet
2776        // (newly-published packages from the simulation appear there), with the
2777        // node's backing package store as fallback for already-on-chain packages.
2778        let mut layout_resolver = epoch_store.executor().type_layout_resolver(
2779            epoch_store.protocol_config(),
2780            Box::new(OverlayBackingPackageStore::new(
2781                &sim.objects,
2782                self.get_backing_package_store(),
2783            )),
2784        );
2785
2786        DevInspectResults::new(
2787            sim.effects,
2788            sim.events.unwrap_or_default(),
2789            sim.execution_result,
2790            raw_txn_data,
2791            raw_effects,
2792            layout_resolver.as_mut(),
2793        )
2794    }
2795
2796    // Only used for testing because of how epoch store is loaded.
2797    pub fn reference_gas_price_for_testing(&self) -> Result<u64, anyhow::Error> {
2798        let epoch_store = self.epoch_store_for_testing();
2799        Ok(epoch_store.reference_gas_price())
2800    }
2801
2802    pub fn is_tx_already_executed(&self, digest: &TransactionDigest) -> bool {
2803        self.get_transaction_cache_reader()
2804            .is_tx_already_executed(digest)
2805    }
2806
2807    #[instrument(level = "debug", skip_all, err(level = "debug"))]
2808    fn index_tx(
2809        sequence: u64,
2810        backing_package_store: &Arc<dyn BackingPackageStore + Send + Sync>,
2811        object_store: &Arc<dyn ObjectStore + Send + Sync>,
2812        indexes: &IndexStore,
2813        digest: &TransactionDigest,
2814        // TODO: index_tx really just need the transaction data here.
2815        cert: &VerifiedExecutableTransaction,
2816        effects: &TransactionEffects,
2817        events: &TransactionEvents,
2818        timestamp_ms: u64,
2819        tx_coins: Option<TxCoins>,
2820        written: &WrittenObjects,
2821        inner_temporary_store: &InnerTemporaryStore,
2822        epoch_store: &Arc<AuthorityPerEpochStore>,
2823        acquire_locks: bool,
2824    ) -> SuiResult<(StagedBatch, IndexStoreCacheUpdatesWithLocks)> {
2825        let changes = Self::process_object_index(backing_package_store, object_store, effects, written, inner_temporary_store, epoch_store)
2826            .tap_err(|e| warn!(tx_digest=?digest, "Failed to process object index, index_tx is skipped: {e}"))?;
2827
2828        indexes.index_tx(
2829            sequence,
2830            cert.data().intent_message().value.sender(),
2831            cert.data()
2832                .intent_message()
2833                .value
2834                .input_objects()?
2835                .iter()
2836                .map(|o| o.object_id()),
2837            effects
2838                .all_changed_objects()
2839                .into_iter()
2840                .map(|(obj_ref, owner, _kind)| (obj_ref, owner)),
2841            cert.data()
2842                .intent_message()
2843                .value
2844                .move_calls()
2845                .into_iter()
2846                .map(|(_cmd_idx, package, module, function)| {
2847                    (*package, module.to_owned(), function.to_owned())
2848                }),
2849            events,
2850            changes,
2851            digest,
2852            timestamp_ms,
2853            tx_coins,
2854            effects.accumulator_events(),
2855            acquire_locks,
2856        )
2857    }
2858
2859    #[cfg(msim)]
2860    fn simulate_fork_during_execution(
2861        &self,
2862        certificate: &VerifiedExecutableTransaction,
2863        epoch_store: &Arc<AuthorityPerEpochStore>,
2864        effects: &mut TransactionEffects,
2865        forked_validators: std::sync::Arc<
2866            std::sync::Mutex<std::collections::HashSet<sui_types::base_types::AuthorityName>>,
2867        >,
2868        full_halt: bool,
2869        effects_overrides: std::sync::Arc<
2870            std::sync::Mutex<std::collections::BTreeMap<String, String>>,
2871        >,
2872        fork_probability: f32,
2873    ) {
2874        static TOTAL_FAILING_STAKE: std::sync::Mutex<u64> = std::sync::Mutex::new(0);
2875        if !certificate.data().intent_message().value.is_system_tx() {
2876            let committee = epoch_store.committee();
2877            let cur_stake = (**committee).weight(&self.name);
2878            if cur_stake > 0 {
2879                {
2880                    let mut total_stake = TOTAL_FAILING_STAKE.lock().unwrap();
2881                    let total_stake = &mut *total_stake;
2882                    let already_forked = forked_validators
2883                        .lock()
2884                        .ok()
2885                        .map(|set| set.contains(&self.name))
2886                        .unwrap_or(false);
2887
2888                    if !already_forked {
2889                        let should_fork = if full_halt {
2890                            // For full halt, fork enough nodes to reach validity threshold
2891                            *total_stake <= committee.validity_threshold()
2892                        } else {
2893                            // For partial fork, stay strictly below validity threshold
2894                            *total_stake + cur_stake < committee.validity_threshold()
2895                        };
2896
2897                        if should_fork {
2898                            *total_stake += cur_stake;
2899
2900                            if let Ok(mut external_set) = forked_validators.lock() {
2901                                external_set.insert(self.name);
2902                                info!("forked_validators: {:?}", external_set);
2903                            }
2904                        }
2905                    }
2906
2907                    if let Ok(external_set) = forked_validators.lock() {
2908                        if external_set.contains(&self.name) {
2909                            // If effects_overrides is empty, deterministically select a tx_digest to fork with 1/100 probability.
2910                            // Fork this transaction and record the digest and the original effects to original_effects.
2911                            // If original_effects is nonempty and contains a key matching this transaction digest (i.e.
2912                            // the transaction was forked on a different validator), fork this txn as well.
2913
2914                            let tx_digest = certificate.digest().to_string();
2915                            if let Ok(mut overrides) = effects_overrides.lock() {
2916                                if overrides.contains_key(&tx_digest)
2917                                    || overrides.is_empty()
2918                                        && sui_simulator::random::deterministic_probability(
2919                                            &tx_digest,
2920                                            fork_probability,
2921                                        )
2922                                {
2923                                    let original_effects_digest = effects.digest().to_string();
2924                                    overrides
2925                                        .insert(tx_digest.clone(), original_effects_digest.clone());
2926                                    info!(
2927                                        ?tx_digest,
2928                                        ?original_effects_digest,
2929                                        "Captured forked effects digest for transaction"
2930                                    );
2931                                    effects.gas_cost_summary_mut_for_testing().computation_cost +=
2932                                        1;
2933                                }
2934                            }
2935                        }
2936                    }
2937                }
2938            }
2939        }
2940    }
2941
2942    fn process_object_index(
2943        backing_package_store: &Arc<dyn BackingPackageStore + Send + Sync>,
2944        object_store: &Arc<dyn ObjectStore + Send + Sync>,
2945        effects: &TransactionEffects,
2946        written: &WrittenObjects,
2947        inner_temporary_store: &InnerTemporaryStore,
2948        epoch_store: &Arc<AuthorityPerEpochStore>,
2949    ) -> SuiResult<ObjectIndexChanges> {
2950        let mut layout_resolver = epoch_store.executor().type_layout_resolver(
2951            epoch_store.protocol_config(),
2952            Box::new(PackageStoreWithFallback::new(
2953                inner_temporary_store,
2954                backing_package_store,
2955            )),
2956        );
2957
2958        let modified_at_version = effects
2959            .modified_at_versions()
2960            .into_iter()
2961            .collect::<HashMap<_, _>>();
2962
2963        let tx_digest = effects.transaction_digest();
2964        let mut deleted_owners = vec![];
2965        let mut deleted_dynamic_fields = vec![];
2966        for (id, _, _) in effects.deleted().into_iter().chain(effects.wrapped()) {
2967            let old_version = modified_at_version.get(&id).unwrap();
2968            // When we process the index, the latest object hasn't been written yet so
2969            // the old object must be present.
2970            match Self::get_owner_at_version(object_store, &id, *old_version).unwrap_or_else(
2971                |e| panic!("tx_digest={:?}, error processing object owner index, cannot find owner for object {:?} at version {:?}. Err: {:?}", tx_digest, id, old_version, e),
2972            ) {
2973                Owner::AddressOwner(addr)
2974                | Owner::ConsensusAddressOwner { owner: addr, .. } => deleted_owners.push((addr, id)),
2975                Owner::ObjectOwner(object_id) => {
2976                    deleted_dynamic_fields.push((ObjectID::from(object_id), id))
2977                }
2978                _ => {}
2979            }
2980        }
2981
2982        let mut new_owners = vec![];
2983        let mut new_dynamic_fields = vec![];
2984
2985        for (oref, owner, kind) in effects.all_changed_objects() {
2986            let id = &oref.0;
2987            // For mutated objects, retrieve old owner and delete old index if there is a owner change.
2988            if let WriteKind::Mutate = kind {
2989                let Some(old_version) = modified_at_version.get(id) else {
2990                    panic!(
2991                        "tx_digest={:?}, error processing object owner index, cannot find modified at version for mutated object [{id}].",
2992                        tx_digest
2993                    );
2994                };
2995                // When we process the index, the latest object hasn't been written yet so
2996                // the old object must be present.
2997                let Some(old_object) = object_store.get_object_by_key(id, *old_version) else {
2998                    panic!(
2999                        "tx_digest={:?}, error processing object owner index, cannot find owner for object {:?} at version {:?}",
3000                        tx_digest, id, old_version
3001                    );
3002                };
3003                if old_object.owner != owner {
3004                    match old_object.owner {
3005                        Owner::AddressOwner(addr)
3006                        | Owner::ConsensusAddressOwner { owner: addr, .. } => {
3007                            deleted_owners.push((addr, *id));
3008                        }
3009                        Owner::ObjectOwner(object_id) => {
3010                            deleted_dynamic_fields.push((ObjectID::from(object_id), *id))
3011                        }
3012                        _ => {}
3013                    }
3014                }
3015            }
3016
3017            match owner {
3018                Owner::AddressOwner(addr) | Owner::ConsensusAddressOwner { owner: addr, .. } => {
3019                    // TODO: We can remove the object fetching after we added ObjectType to TransactionEffects
3020                    let new_object = written.get(id).unwrap_or_else(
3021                        || panic!("tx_digest={:?}, error processing object owner index, written does not contain object {:?}", tx_digest, id)
3022                    );
3023                    assert_eq!(
3024                        new_object.version(),
3025                        oref.1,
3026                        "tx_digest={:?} error processing object owner index, object {:?} from written has mismatched version. Actual: {}, expected: {}",
3027                        tx_digest,
3028                        id,
3029                        new_object.version(),
3030                        oref.1
3031                    );
3032
3033                    let type_ = new_object
3034                        .type_()
3035                        .map(|type_| ObjectType::Struct(type_.clone()))
3036                        .unwrap_or(ObjectType::Package);
3037
3038                    new_owners.push((
3039                        (addr, *id),
3040                        ObjectInfo {
3041                            object_id: *id,
3042                            version: oref.1,
3043                            digest: oref.2,
3044                            type_,
3045                            owner,
3046                            previous_transaction: *effects.transaction_digest(),
3047                        },
3048                    ));
3049                }
3050                Owner::ObjectOwner(owner) => {
3051                    let new_object = written.get(id).unwrap_or_else(
3052                        || panic!("tx_digest={:?}, error processing object owner index, written does not contain object {:?}", tx_digest, id)
3053                    );
3054                    assert_eq!(
3055                        new_object.version(),
3056                        oref.1,
3057                        "tx_digest={:?} error processing object owner index, object {:?} from written has mismatched version. Actual: {}, expected: {}",
3058                        tx_digest,
3059                        id,
3060                        new_object.version(),
3061                        oref.1
3062                    );
3063
3064                    let Some(df_info) = Self::try_create_dynamic_field_info(
3065                        object_store,
3066                        new_object,
3067                        written,
3068                        layout_resolver.as_mut(),
3069                    )
3070                    .unwrap_or_else(|e| {
3071                        error!(
3072                            "try_create_dynamic_field_info should not fail, {}, new_object={:?}",
3073                            e, new_object
3074                        );
3075                        None
3076                    }) else {
3077                        // Skip indexing for non dynamic field objects.
3078                        continue;
3079                    };
3080                    new_dynamic_fields.push(((ObjectID::from(owner), *id), df_info))
3081                }
3082                _ => {}
3083            }
3084        }
3085
3086        Ok(ObjectIndexChanges {
3087            deleted_owners,
3088            deleted_dynamic_fields,
3089            new_owners,
3090            new_dynamic_fields,
3091        })
3092    }
3093
3094    fn try_create_dynamic_field_info(
3095        object_store: &Arc<dyn ObjectStore + Send + Sync>,
3096        o: &Object,
3097        written: &WrittenObjects,
3098        resolver: &mut dyn LayoutResolver,
3099    ) -> SuiResult<Option<DynamicFieldInfo>> {
3100        // Skip if not a move object
3101        let Some(move_object) = o.data.try_as_move().cloned() else {
3102            return Ok(None);
3103        };
3104
3105        // We only index dynamic field objects
3106        if !move_object.type_().is_dynamic_field() {
3107            return Ok(None);
3108        }
3109
3110        let layout = resolver
3111            .get_annotated_layout(&move_object.type_().clone().into())?
3112            .into_layout();
3113
3114        let field =
3115            DFV::FieldVisitor::deserialize(move_object.contents(), &layout).map_err(|e| {
3116                SuiErrorKind::ObjectDeserializationError {
3117                    error: e.to_string(),
3118                }
3119            })?;
3120
3121        let type_ = field.kind;
3122        let name_type: TypeTag = field.name_layout.into();
3123        let bcs_name = field.name_bytes.to_owned();
3124
3125        let name_value = BoundedVisitor::deserialize_value(field.name_bytes, field.name_layout)
3126            .map_err(|e| {
3127                warn!("{e}");
3128                SuiErrorKind::ObjectDeserializationError {
3129                    error: e.to_string(),
3130                }
3131            })?;
3132
3133        let name = DynamicFieldName {
3134            type_: name_type,
3135            value: SuiMoveValue::from(name_value).to_json_value(),
3136        };
3137
3138        let value_metadata = field.value_metadata().map_err(|e| {
3139            warn!("{e}");
3140            SuiErrorKind::ObjectDeserializationError {
3141                error: e.to_string(),
3142            }
3143        })?;
3144
3145        Ok(Some(match value_metadata {
3146            DFV::ValueMetadata::DynamicField(object_type) => DynamicFieldInfo {
3147                name,
3148                bcs_name,
3149                type_,
3150                object_type: object_type.to_canonical_string(/* with_prefix */ true),
3151                object_id: o.id(),
3152                version: o.version(),
3153                digest: o.digest(),
3154            },
3155
3156            DFV::ValueMetadata::DynamicObjectField(object_id) => {
3157                // Find the actual object from storage using the object id obtained from the wrapper.
3158
3159                // Try to find the object in the written objects first.
3160                let (version, digest, object_type) = if let Some(object) = written.get(&object_id) {
3161                    let version = object.version();
3162                    let digest = object.digest();
3163                    let object_type = object.data.type_().unwrap().clone();
3164                    (version, digest, object_type)
3165                } else {
3166                    // If not found, try to find it in the database.
3167                    let object = object_store
3168                        .get_object_by_key(&object_id, o.version())
3169                        .ok_or_else(|| UserInputError::ObjectNotFound {
3170                            object_id,
3171                            version: Some(o.version()),
3172                        })?;
3173                    let version = object.version();
3174                    let digest = object.digest();
3175                    let object_type = object.data.type_().unwrap().clone();
3176                    (version, digest, object_type)
3177                };
3178
3179                DynamicFieldInfo {
3180                    name,
3181                    bcs_name,
3182                    type_,
3183                    object_type: object_type.to_string(),
3184                    object_id,
3185                    version,
3186                    digest,
3187                }
3188            }
3189        }))
3190    }
3191
3192    #[instrument(level = "trace", skip_all, err(level = "debug"))]
3193    fn post_process_one_tx(
3194        &self,
3195        certificate: &VerifiedExecutableTransaction,
3196        effects: &TransactionEffects,
3197        inner_temporary_store: &InnerTemporaryStore,
3198        epoch_store: &Arc<AuthorityPerEpochStore>,
3199    ) -> SuiResult {
3200        let Some(indexes) = &self.indexes else {
3201            return Ok(());
3202        };
3203
3204        let tx_digest = *certificate.digest();
3205
3206        // Allocate sequence number on the calling thread to preserve execution order.
3207        let sequence = indexes.allocate_sequence_number();
3208
3209        if self.config.sync_post_process_one_tx {
3210            // Synchronous mode: run post-processing inline on the calling thread
3211            // and commit the index batch immediately with locks held.
3212            // Used as a rollback mechanism and for testing correctness against async mode.
3213            // TODO: delete this branch once async mode has shipped
3214            let result = Self::post_process_one_tx_impl(
3215                sequence,
3216                indexes,
3217                &self.subscription_handler,
3218                &self.metrics,
3219                self.name,
3220                self.get_backing_package_store(),
3221                self.get_object_store(),
3222                certificate,
3223                effects,
3224                inner_temporary_store,
3225                epoch_store,
3226                true, // acquire_locks
3227            );
3228
3229            match result {
3230                Ok((raw_batch, cache_updates_with_locks)) => {
3231                    let mut db_batch = indexes.new_db_batch();
3232                    db_batch
3233                        .concat(vec![raw_batch])
3234                        .expect("failed to absorb raw index batch");
3235                    // Destructure to keep _locks alive through commit_index_batch.
3236                    let IndexStoreCacheUpdatesWithLocks { _locks, inner } =
3237                        cache_updates_with_locks;
3238                    indexes
3239                        .commit_index_batch(db_batch, vec![inner])
3240                        .expect("failed to commit index batch");
3241                }
3242                Err(e) => {
3243                    self.metrics.post_processing_total_failures.inc();
3244                    error!(?tx_digest, "tx post processing failed: {e}");
3245                    return Err(e);
3246                }
3247            }
3248
3249            return Ok(());
3250        }
3251
3252        let (done_tx, done_rx) = tokio::sync::oneshot::channel::<PostProcessingOutput>();
3253        self.pending_post_processing.insert(tx_digest, done_rx);
3254
3255        let indexes = indexes.clone();
3256        let subscription_handler = self.subscription_handler.clone();
3257        let metrics = self.metrics.clone();
3258        let name = self.name;
3259        let backing_package_store = self.get_backing_package_store().clone();
3260        let object_store = self.get_object_store().clone();
3261        let semaphore = self.post_processing_semaphore.clone();
3262
3263        let certificate = certificate.clone();
3264        let effects = effects.clone();
3265        let inner_temporary_store = inner_temporary_store.clone();
3266        let epoch_store = epoch_store.clone();
3267
3268        // spawn post processing on a blocking thread
3269        tokio::spawn(async move {
3270            let permit = {
3271                let _scope = monitored_scope("Execution::post_process_one_tx::semaphore_acquire");
3272                semaphore
3273                    .acquire_owned()
3274                    .await
3275                    .expect("post-processing semaphore should not be closed")
3276            };
3277
3278            let _ = tokio::task::spawn_blocking(move || {
3279                let _permit = permit;
3280
3281                let result = Self::post_process_one_tx_impl(
3282                    sequence,
3283                    &indexes,
3284                    &subscription_handler,
3285                    &metrics,
3286                    name,
3287                    &backing_package_store,
3288                    &object_store,
3289                    &certificate,
3290                    &effects,
3291                    &inner_temporary_store,
3292                    &epoch_store,
3293                    false, // acquire_locks
3294                );
3295
3296                match result {
3297                    Ok((raw_batch, cache_updates_with_locks)) => {
3298                        fail_point!("crash-after-post-process-one-tx");
3299                        let output = (raw_batch, cache_updates_with_locks.into_inner());
3300                        let _ = done_tx.send(output);
3301                    }
3302                    Err(e) => {
3303                        metrics.post_processing_total_failures.inc();
3304                        error!(?tx_digest, "tx post processing failed: {e}");
3305                    }
3306                }
3307            })
3308            .await;
3309        });
3310
3311        Ok(())
3312    }
3313
3314    fn post_process_one_tx_impl(
3315        sequence: u64,
3316        indexes: &Arc<IndexStore>,
3317        subscription_handler: &Arc<SubscriptionHandler>,
3318        metrics: &Arc<AuthorityMetrics>,
3319        name: AuthorityName,
3320        backing_package_store: &Arc<dyn BackingPackageStore + Send + Sync>,
3321        object_store: &Arc<dyn ObjectStore + Send + Sync>,
3322        certificate: &VerifiedExecutableTransaction,
3323        effects: &TransactionEffects,
3324        inner_temporary_store: &InnerTemporaryStore,
3325        epoch_store: &Arc<AuthorityPerEpochStore>,
3326        acquire_locks: bool,
3327    ) -> SuiResult<(StagedBatch, IndexStoreCacheUpdatesWithLocks)> {
3328        let _scope = monitored_scope("Execution::post_process_one_tx");
3329
3330        let tx_digest = certificate.digest();
3331        let timestamp_ms = Self::unixtime_now_ms();
3332        let events = &inner_temporary_store.events;
3333        let written = &inner_temporary_store.written;
3334        let tx_coins = Self::fullnode_only_get_tx_coins_for_indexing(
3335            name,
3336            object_store,
3337            effects,
3338            inner_temporary_store,
3339            epoch_store,
3340        );
3341
3342        let (raw_batch, cache_updates) = Self::index_tx(
3343            sequence,
3344            backing_package_store,
3345            object_store,
3346            indexes,
3347            tx_digest,
3348            certificate,
3349            effects,
3350            events,
3351            timestamp_ms,
3352            tx_coins,
3353            written,
3354            inner_temporary_store,
3355            epoch_store,
3356            acquire_locks,
3357        )
3358        .tap_ok(|_| metrics.post_processing_total_tx_indexed.inc())
3359        .tap_err(|e| error!(?tx_digest, "Post processing - Couldn't index tx: {e}"))
3360        .expect("Indexing tx should not fail");
3361
3362        let effects: SuiTransactionBlockEffects = effects.clone().try_into()?;
3363        let events = Self::make_transaction_block_events(
3364            backing_package_store,
3365            events.clone(),
3366            *tx_digest,
3367            timestamp_ms,
3368            epoch_store,
3369            inner_temporary_store,
3370        )?;
3371        // Emit events
3372        subscription_handler
3373            .process_tx(certificate.data().transaction_data(), &effects, &events)
3374            .tap_ok(|_| metrics.post_processing_total_tx_had_event_processed.inc())
3375            .tap_err(|e| {
3376                warn!(
3377                    ?tx_digest,
3378                    "Post processing - Couldn't process events for tx: {}", e
3379                )
3380            })?;
3381
3382        metrics
3383            .post_processing_total_events_emitted
3384            .inc_by(events.data.len() as u64);
3385
3386        Ok((raw_batch, cache_updates))
3387    }
3388
3389    fn make_transaction_block_events(
3390        backing_package_store: &Arc<dyn BackingPackageStore + Send + Sync>,
3391        transaction_events: TransactionEvents,
3392        digest: TransactionDigest,
3393        timestamp_ms: u64,
3394        epoch_store: &Arc<AuthorityPerEpochStore>,
3395        inner_temporary_store: &InnerTemporaryStore,
3396    ) -> SuiResult<SuiTransactionBlockEvents> {
3397        let mut layout_resolver = epoch_store.executor().type_layout_resolver(
3398            epoch_store.protocol_config(),
3399            Box::new(PackageStoreWithFallback::new(
3400                inner_temporary_store,
3401                backing_package_store,
3402            )),
3403        );
3404        SuiTransactionBlockEvents::try_from(
3405            transaction_events,
3406            digest,
3407            Some(timestamp_ms),
3408            layout_resolver.as_mut(),
3409        )
3410    }
3411
3412    pub fn unixtime_now_ms() -> u64 {
3413        let now = SystemTime::now()
3414            .duration_since(UNIX_EPOCH)
3415            .expect("Time went backwards")
3416            .as_millis();
3417        u64::try_from(now).expect("Travelling in time machine")
3418    }
3419
3420    // TODO(fastpath): update this handler for Mysticeti fastpath.
3421    // There will no longer be validator quorum signed transactions or effects.
3422    // The proof of finality needs to come from checkpoints.
3423    #[instrument(level = "trace", skip_all)]
3424    pub async fn handle_transaction_info_request(
3425        &self,
3426        request: TransactionInfoRequest,
3427    ) -> SuiResult<TransactionInfoResponse> {
3428        let epoch_store = self.load_epoch_store_one_call_per_task();
3429        let (transaction, status) = self
3430            .get_transaction_status(&request.transaction_digest, &epoch_store)?
3431            .ok_or(SuiErrorKind::TransactionNotFound {
3432                digest: request.transaction_digest,
3433            })?;
3434        Ok(TransactionInfoResponse {
3435            transaction,
3436            status,
3437        })
3438    }
3439
3440    #[instrument(level = "trace", skip_all)]
3441    pub async fn handle_object_info_request(
3442        &self,
3443        request: ObjectInfoRequest,
3444    ) -> SuiResult<ObjectInfoResponse> {
3445        let requested_object_seq = match request.request_kind {
3446            ObjectInfoRequestKind::LatestObjectInfo => {
3447                let (_, seq, _) =
3448                    self.get_object_or_tombstone(request.object_id)
3449                        .ok_or_else(|| {
3450                            SuiError::from(UserInputError::ObjectNotFound {
3451                                object_id: request.object_id,
3452                                version: None,
3453                            })
3454                        })?;
3455                seq
3456            }
3457            ObjectInfoRequestKind::PastObjectInfoDebug(seq) => seq,
3458        };
3459
3460        let object = self
3461            .get_object_store()
3462            .get_object_by_key(&request.object_id, requested_object_seq)
3463            .ok_or_else(|| {
3464                SuiError::from(UserInputError::ObjectNotFound {
3465                    object_id: request.object_id,
3466                    version: Some(requested_object_seq),
3467                })
3468            })?;
3469
3470        let layout = if let (LayoutGenerationOption::Generate, Some(move_obj)) =
3471            (request.generate_layout, object.data.try_as_move())
3472        {
3473            let epoch_store = self.load_epoch_store_one_call_per_task();
3474            Some(into_struct_layout(
3475                epoch_store
3476                    .executor()
3477                    .type_layout_resolver(
3478                        epoch_store.protocol_config(),
3479                        Box::new(self.get_backing_package_store().as_ref()),
3480                    )
3481                    .get_annotated_layout(&move_obj.type_().clone().into())?,
3482            )?)
3483        } else {
3484            None
3485        };
3486
3487        Ok(ObjectInfoResponse {
3488            object,
3489            layout,
3490            // Validators no longer store signed transactions, so the locking
3491            // transaction cannot be returned.
3492            lock_for_debugging: None,
3493        })
3494    }
3495
3496    #[instrument(level = "trace", skip_all)]
3497    pub fn handle_checkpoint_request(
3498        &self,
3499        request: &CheckpointRequest,
3500    ) -> SuiResult<CheckpointResponse> {
3501        let summary = match request.sequence_number {
3502            Some(seq) => self
3503                .checkpoint_store
3504                .get_checkpoint_by_sequence_number(seq)?,
3505            None => self.checkpoint_store.get_latest_certified_checkpoint()?,
3506        }
3507        .map(|v| v.into_inner());
3508        let contents = match &summary {
3509            Some(s) => self
3510                .checkpoint_store
3511                .get_checkpoint_contents(&s.content_digest)?,
3512            None => None,
3513        };
3514        Ok(CheckpointResponse {
3515            checkpoint: summary,
3516            contents,
3517        })
3518    }
3519
3520    #[instrument(level = "trace", skip_all)]
3521    pub fn handle_checkpoint_request_v2(
3522        &self,
3523        request: &CheckpointRequestV2,
3524    ) -> SuiResult<CheckpointResponseV2> {
3525        let summary = if request.certified {
3526            let summary = match request.sequence_number {
3527                Some(seq) => self
3528                    .checkpoint_store
3529                    .get_checkpoint_by_sequence_number(seq)?,
3530                None => self.checkpoint_store.get_latest_certified_checkpoint()?,
3531            }
3532            .map(|v| v.into_inner());
3533            summary.map(CheckpointSummaryResponse::Certified)
3534        } else {
3535            let summary = match request.sequence_number {
3536                Some(seq) => self.checkpoint_store.get_locally_computed_checkpoint(seq)?,
3537                None => self
3538                    .checkpoint_store
3539                    .get_latest_locally_computed_checkpoint()?,
3540            };
3541            summary.map(CheckpointSummaryResponse::Pending)
3542        };
3543        let contents = match &summary {
3544            Some(s) => self
3545                .checkpoint_store
3546                .get_checkpoint_contents(&s.content_digest())?,
3547            None => None,
3548        };
3549        Ok(CheckpointResponseV2 {
3550            checkpoint: summary,
3551            contents,
3552        })
3553    }
3554
3555    fn check_protocol_version(
3556        supported_protocol_versions: SupportedProtocolVersions,
3557        current_version: ProtocolVersion,
3558    ) {
3559        info!("current protocol version is now {:?}", current_version);
3560        info!("supported versions are: {:?}", supported_protocol_versions);
3561        if !supported_protocol_versions.is_version_supported(current_version) {
3562            let msg = format!(
3563                "Unsupported protocol version. The network is at {:?}, but this SuiNode only supports: {:?}. Shutting down.",
3564                current_version, supported_protocol_versions,
3565            );
3566
3567            error!("{}", msg);
3568            eprintln!("{}", msg);
3569
3570            #[cfg(not(msim))]
3571            std::process::exit(1);
3572
3573            #[cfg(msim)]
3574            sui_simulator::task::shutdown_current_node();
3575        }
3576    }
3577
3578    #[allow(clippy::disallowed_methods)] // allow unbounded_channel()
3579    #[allow(clippy::too_many_arguments)]
3580    pub async fn new(
3581        name: AuthorityName,
3582        secret: StableSyncAuthoritySigner,
3583        supported_protocol_versions: SupportedProtocolVersions,
3584        store: Arc<AuthorityStore>,
3585        execution_cache_trait_pointers: ExecutionCacheTraitPointers,
3586        epoch_store: Arc<AuthorityPerEpochStore>,
3587        committee_store: Arc<CommitteeStore>,
3588        indexes: Option<Arc<IndexStore>>,
3589        rpc_store: Option<RpcStore>,
3590        checkpoint_store: Arc<CheckpointStore>,
3591        prometheus_registry: &Registry,
3592        genesis_objects: &[Object],
3593        db_checkpoint_config: &DBCheckpointConfig,
3594        config: NodeConfig,
3595        chain_identifier: ChainIdentifier,
3596        policy_config: Option<PolicyConfig>,
3597        firewall_config: Option<RemoteFirewallConfig>,
3598        pruner_watermarks: Arc<PrunerWatermarks>,
3599    ) -> Arc<Self> {
3600        Self::check_protocol_version(supported_protocol_versions, epoch_store.protocol_version());
3601
3602        let metrics = Arc::new(AuthorityMetrics::new(prometheus_registry));
3603        let (tx_ready_certificates, rx_ready_certificates) = unbounded_channel();
3604        let execution_scheduler = Arc::new(ExecutionScheduler::new(
3605            execution_cache_trait_pointers.object_cache_reader.clone(),
3606            execution_cache_trait_pointers.account_funds_read.clone(),
3607            execution_cache_trait_pointers
3608                .transaction_cache_reader
3609                .clone(),
3610            tx_ready_certificates,
3611            &epoch_store,
3612            config.funds_withdraw_scheduler_type,
3613            metrics.clone(),
3614            prometheus_registry,
3615        ));
3616        let (tx_execution_shutdown, rx_execution_shutdown) = oneshot::channel();
3617
3618        let _authority_per_epoch_pruner = AuthorityPerEpochStorePruner::new(
3619            epoch_store.get_parent_path(),
3620            &config.authority_store_pruning_config,
3621        );
3622        let _pruner = AuthorityStorePruner::new(
3623            store.perpetual_tables.clone(),
3624            checkpoint_store.clone(),
3625            rpc_store,
3626            indexes.clone(),
3627            config.authority_store_pruning_config.clone(),
3628            epoch_store.committee().authority_exists(&name),
3629            epoch_store.epoch_start_state().epoch_duration_ms(),
3630            prometheus_registry,
3631            pruner_watermarks,
3632        );
3633        let input_loader =
3634            TransactionInputLoader::new(execution_cache_trait_pointers.object_cache_reader.clone());
3635        let epoch = epoch_store.epoch();
3636        let traffic_controller_metrics =
3637            Arc::new(TrafficControllerMetrics::new(prometheus_registry));
3638        let traffic_controller = if let Some(policy_config) = policy_config {
3639            Some(Arc::new(
3640                TrafficController::init(
3641                    policy_config,
3642                    traffic_controller_metrics,
3643                    firewall_config.clone(),
3644                )
3645                .await,
3646            ))
3647        } else {
3648            None
3649        };
3650
3651        let fork_recovery_state = config.fork_recovery.as_ref().map(|fork_config| {
3652            ForkRecoveryState::new(Some(fork_config))
3653                .expect("Failed to initialize fork recovery state")
3654        });
3655
3656        let coin_reservation_resolver = Arc::new(CachingCoinReservationResolver::new(
3657            execution_cache_trait_pointers
3658                .runtime_object_resolver
3659                .clone(),
3660        ));
3661
3662        let object_funds_checker_metrics =
3663            Arc::new(ObjectFundsCheckerMetrics::new(prometheus_registry));
3664
3665        let transaction_deny_config_manager = TransactionDenyConfigManager::new(
3666            name,
3667            config.transaction_deny_config.clone(),
3668            config.peer_deny_sync_config.clone(),
3669            epoch_store.committee().clone(),
3670            store.perpetual_tables.clone(),
3671            prometheus_registry,
3672        )
3673        .expect("Failed to initialize TransactionDenyConfigManager");
3674        // Drop any cached entries from peers no longer in the active committee.
3675        if let Err(e) =
3676            transaction_deny_config_manager.update_for_committee(epoch_store.committee().clone())
3677        {
3678            warn!(
3679                "Initial update_for_committee failed during AuthorityState init: {:?}",
3680                e
3681            );
3682        }
3683
3684        let state = Arc::new(AuthorityState {
3685            name,
3686            secret,
3687            execution_lock: RwLock::new(epoch),
3688            epoch_store: ArcSwap::new(epoch_store.clone()),
3689            input_loader,
3690            execution_cache_trait_pointers,
3691            coin_reservation_resolver,
3692            indexes,
3693            subscription_handler: Arc::new(SubscriptionHandler::new(prometheus_registry)),
3694            checkpoint_store,
3695            committee_store,
3696            execution_scheduler,
3697            tx_execution_shutdown: Mutex::new(Some(tx_execution_shutdown)),
3698            metrics,
3699            _pruner,
3700            _authority_per_epoch_pruner,
3701            db_checkpoint_config: db_checkpoint_config.clone(),
3702            config,
3703            overload_info: AuthorityOverloadInfo::default(),
3704            chain_identifier,
3705            congestion_tracker: Arc::new(CongestionTracker::new()),
3706            consensus_gasless_counter: Arc::new(ConsensusGaslessCounter::default()),
3707            traffic_controller,
3708            fork_recovery_state,
3709            notify_epoch: tokio::sync::watch::channel(epoch).0,
3710            object_funds_checker: ArcSwapOption::empty(),
3711            // unsettled_object_withdrawals needs to be initialized unconditionally, even on fullnodes.
3712            // Once we enable object funds checking during execution, fullnodes will need it to track
3713            // unsettled object withdraws as well similar to validators.
3714            unsettled_object_withdrawals: Arc::new(UnsettledObjectWithdrawals::new(
3715                object_funds_checker_metrics.clone(),
3716            )),
3717            object_funds_checker_metrics,
3718            pending_post_processing: Arc::new(DashMap::new()),
3719            post_processing_semaphore: Arc::new(tokio::sync::Semaphore::new(num_cpus::get())),
3720            transaction_deny_config_manager,
3721        });
3722        state.init_object_funds_checker().await;
3723
3724        // Start a task to execute ready certificates.
3725        let authority_state = Arc::downgrade(&state);
3726        spawn_monitored_task!(execution_process(
3727            authority_state,
3728            rx_ready_certificates,
3729            rx_execution_shutdown,
3730        ));
3731        // TODO: This doesn't belong to the constructor of AuthorityState.
3732        state
3733            .create_owner_index_if_empty(genesis_objects, &epoch_store)
3734            .expect("Error indexing genesis objects.");
3735
3736        if epoch_store
3737            .protocol_config()
3738            .enable_multi_epoch_transaction_expiration()
3739            && epoch_store.epoch() > 0
3740        {
3741            use typed_store::Map;
3742            let previous_epoch = epoch_store.epoch() - 1;
3743            let start_key = (previous_epoch, TransactionDigest::ZERO);
3744            let end_key = (previous_epoch + 1, TransactionDigest::ZERO);
3745            let has_previous_epoch_data = store
3746                .perpetual_tables
3747                .executed_transaction_digests
3748                .safe_range_iter(start_key..end_key)
3749                .next()
3750                .is_some();
3751
3752            if !has_previous_epoch_data {
3753                panic!(
3754                    "enable_multi_epoch_transaction_expiration is enabled but no transaction data found for previous epoch {}. \
3755                    This indicates the node was restored using an old version of sui-tool that does not backfill the table. \
3756                    Please restore from a snapshot using the latest version of sui-tool.",
3757                    previous_epoch
3758                );
3759            }
3760        }
3761
3762        state
3763    }
3764
3765    async fn init_object_funds_checker(&self) {
3766        let epoch_store = self.epoch_store.load();
3767        // TODO: Once we enable object funds checking during execution, we will no longer need to initialize the object funds checker here.
3768        if self.node_role(&epoch_store).runs_consensus()
3769            && epoch_store.protocol_config().enable_object_funds_withdraw()
3770        {
3771            if self.object_funds_checker.load().is_none() {
3772                let inner = self.get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID).map(|o| {
3773                    Arc::new(ObjectFundsChecker::new(
3774                        o.version(),
3775                        self.unsettled_object_withdrawals.clone(),
3776                        self.object_funds_checker_metrics.clone(),
3777                    ))
3778                });
3779                self.object_funds_checker.store(inner);
3780            }
3781        } else {
3782            self.object_funds_checker.store(None);
3783        }
3784    }
3785
3786    // TODO: Consolidate our traits to reduce the number of methods here.
3787    pub fn get_object_cache_reader(&self) -> &Arc<dyn ObjectCacheRead> {
3788        &self.execution_cache_trait_pointers.object_cache_reader
3789    }
3790
3791    pub fn get_transaction_cache_reader(&self) -> &Arc<dyn TransactionCacheRead> {
3792        &self.execution_cache_trait_pointers.transaction_cache_reader
3793    }
3794
3795    pub fn get_cache_writer(&self) -> &Arc<dyn ExecutionCacheWrite> {
3796        &self.execution_cache_trait_pointers.cache_writer
3797    }
3798
3799    pub fn get_backing_store(&self) -> &Arc<dyn BackingStore + Send + Sync> {
3800        &self.execution_cache_trait_pointers.backing_store
3801    }
3802
3803    pub fn get_runtime_object_resolver(&self) -> &Arc<dyn RuntimeObjectResolver + Send + Sync> {
3804        &self.execution_cache_trait_pointers.runtime_object_resolver
3805    }
3806
3807    pub(crate) fn get_account_funds_read(&self) -> &Arc<dyn AccountFundsRead> {
3808        &self.execution_cache_trait_pointers.account_funds_read
3809    }
3810
3811    pub fn get_backing_package_store(&self) -> &Arc<dyn BackingPackageStore + Send + Sync> {
3812        &self.execution_cache_trait_pointers.backing_package_store
3813    }
3814
3815    pub fn get_object_store(&self) -> &Arc<dyn ObjectStore + Send + Sync> {
3816        &self.execution_cache_trait_pointers.object_store
3817    }
3818
3819    pub async fn await_post_processing(
3820        &self,
3821        tx_digest: &TransactionDigest,
3822    ) -> Option<PostProcessingOutput> {
3823        if let Some((_, rx)) = self.pending_post_processing.remove(tx_digest) {
3824            // Tx was executed and post-processing is in flight.
3825            rx.await.ok()
3826        } else {
3827            // Tx was already persisted or post-processing already completed.
3828            None
3829        }
3830    }
3831
3832    /// Await post-processing for a transaction and commit the index batch immediately.
3833    /// Used in test helpers where there is no CheckpointExecutor to collect and commit
3834    /// index batches at checkpoint boundaries.
3835    pub async fn flush_post_processing(&self, tx_digest: &TransactionDigest) {
3836        if let Some(indexes) = &self.indexes
3837            && let Some((raw_batch, cache_updates)) = self.await_post_processing(tx_digest).await
3838        {
3839            let mut db_batch = indexes.new_db_batch();
3840            db_batch
3841                .concat(vec![raw_batch])
3842                .expect("failed to build index batch");
3843            indexes
3844                .commit_index_batch(db_batch, vec![cache_updates])
3845                .expect("failed to commit index batch");
3846        }
3847    }
3848
3849    pub fn get_reconfig_api(&self) -> &Arc<dyn ExecutionCacheReconfigAPI> {
3850        &self.execution_cache_trait_pointers.reconfig_api
3851    }
3852
3853    pub fn get_global_state_hash_store(&self) -> &Arc<dyn GlobalStateHashStore> {
3854        &self.execution_cache_trait_pointers.global_state_hash_store
3855    }
3856
3857    pub fn get_checkpoint_cache(&self) -> &Arc<dyn CheckpointCache> {
3858        &self.execution_cache_trait_pointers.checkpoint_cache
3859    }
3860
3861    pub fn get_state_sync_store(&self) -> &Arc<dyn StateSyncAPI> {
3862        &self.execution_cache_trait_pointers.state_sync_store
3863    }
3864
3865    pub fn get_cache_commit(&self) -> &Arc<dyn ExecutionCacheCommit> {
3866        &self.execution_cache_trait_pointers.cache_commit
3867    }
3868
3869    pub fn database_for_testing(&self) -> Arc<AuthorityStore> {
3870        self.execution_cache_trait_pointers
3871            .testing_api
3872            .database_for_testing()
3873    }
3874
3875    /// Access to the underlying authority store for diagnostic tooling (db-shell).
3876    ///
3877    /// Goes through `testing_api` deliberately: db-shell is read-only diagnostics
3878    /// that bypasses the writeback cache, and we want the execution path to have
3879    /// no other route to the raw `AuthorityStore`. Using the testing API here
3880    /// keeps that invariant visible — diagnostic code is the only non-test caller.
3881    pub fn authority_store(&self) -> Arc<AuthorityStore> {
3882        self.execution_cache_trait_pointers
3883            .testing_api
3884            .database_for_testing()
3885    }
3886
3887    pub fn cache_for_testing(&self) -> &WritebackCache {
3888        self.execution_cache_trait_pointers
3889            .testing_api
3890            .cache_for_testing()
3891    }
3892
3893    pub async fn prune_checkpoints_for_eligible_epochs_for_testing(
3894        &self,
3895        config: NodeConfig,
3896        metrics: Arc<AuthorityStorePruningMetrics>,
3897    ) -> anyhow::Result<()> {
3898        use crate::authority::authority_store_pruner::PrunerWatermarks;
3899        let watermarks = Arc::new(PrunerWatermarks::default());
3900        AuthorityStorePruner::prune_checkpoints_for_eligible_epochs(
3901            &self.database_for_testing().perpetual_tables,
3902            &self.checkpoint_store,
3903            None,
3904            config.authority_store_pruning_config,
3905            metrics,
3906            EPOCH_DURATION_MS_FOR_TESTING,
3907            &watermarks,
3908        )
3909        .await
3910    }
3911
3912    pub fn execution_scheduler(&self) -> &Arc<ExecutionScheduler> {
3913        &self.execution_scheduler
3914    }
3915
3916    fn create_owner_index_if_empty(
3917        &self,
3918        genesis_objects: &[Object],
3919        epoch_store: &Arc<AuthorityPerEpochStore>,
3920    ) -> SuiResult {
3921        let Some(index_store) = &self.indexes else {
3922            return Ok(());
3923        };
3924        if !index_store.is_empty() {
3925            return Ok(());
3926        }
3927
3928        let mut new_owners = vec![];
3929        let mut new_dynamic_fields = vec![];
3930        let mut layout_resolver = epoch_store.executor().type_layout_resolver(
3931            epoch_store.protocol_config(),
3932            Box::new(self.get_backing_package_store().as_ref()),
3933        );
3934        for o in genesis_objects.iter() {
3935            match o.owner {
3936                Owner::AddressOwner(addr) | Owner::ConsensusAddressOwner { owner: addr, .. } => {
3937                    new_owners.push((
3938                        (addr, o.id()),
3939                        ObjectInfo::new(&o.compute_object_reference(), o),
3940                    ))
3941                }
3942                Owner::ObjectOwner(object_id) => {
3943                    let id = o.id();
3944                    let Some(info) = Self::try_create_dynamic_field_info(
3945                        self.get_object_store(),
3946                        o,
3947                        &BTreeMap::new(),
3948                        layout_resolver.as_mut(),
3949                    )?
3950                    else {
3951                        continue;
3952                    };
3953                    new_dynamic_fields.push(((ObjectID::from(object_id), id), info));
3954                }
3955                _ => {}
3956            }
3957        }
3958
3959        index_store.insert_genesis_objects(ObjectIndexChanges {
3960            deleted_owners: vec![],
3961            deleted_dynamic_fields: vec![],
3962            new_owners,
3963            new_dynamic_fields,
3964        })
3965    }
3966
3967    /// Attempts to acquire execution lock for an executable transaction.
3968    /// Returns Some(lock) if the transaction is matching current executed epoch.
3969    /// Returns None if validator is halted at epoch end or epoch mismatch.
3970    pub fn execution_lock_for_executable_transaction(
3971        &self,
3972        transaction: &VerifiedExecutableTransaction,
3973    ) -> Option<ExecutionLockReadGuard<'_>> {
3974        let lock = self.execution_lock.try_read().ok()?;
3975        if *lock == transaction.auth_sig().epoch() {
3976            Some(lock)
3977        } else {
3978            // TODO: Can this still happen?
3979            None
3980        }
3981    }
3982
3983    /// Acquires the execution lock for the duration of transaction validation.
3984    /// This prevents reconfiguration from starting until we are finished validating the transaction.
3985    fn execution_lock_for_validation(&self) -> SuiResult<ExecutionLockReadGuard<'_>> {
3986        self.execution_lock
3987            .try_read()
3988            .map_err(|_| SuiErrorKind::ValidatorHaltedAtEpochEnd.into())
3989    }
3990
3991    pub async fn execution_lock_for_reconfiguration(&self) -> ExecutionLockWriteGuard<'_> {
3992        self.execution_lock.write().await
3993    }
3994
3995    #[instrument(level = "error", skip_all)]
3996    pub async fn reconfigure(
3997        &self,
3998        cur_epoch_store: &AuthorityPerEpochStore,
3999        supported_protocol_versions: SupportedProtocolVersions,
4000        new_committee: Committee,
4001        epoch_start_configuration: EpochStartConfiguration,
4002        state_hasher: Arc<GlobalStateHasher>,
4003        expensive_safety_check_config: &ExpensiveSafetyCheckConfig,
4004        epoch_last_checkpoint: CheckpointSequenceNumber,
4005    ) -> SuiResult<Arc<AuthorityPerEpochStore>> {
4006        Self::check_protocol_version(
4007            supported_protocol_versions,
4008            epoch_start_configuration
4009                .epoch_start_state()
4010                .protocol_version(),
4011        );
4012
4013        self.committee_store.insert_new_committee(&new_committee)?;
4014
4015        // Wait until no transactions are being executed.
4016        let mut execution_lock = self.execution_lock_for_reconfiguration().await;
4017
4018        // Terminate all epoch-specific tasks (those started with within_alive_epoch).
4019        cur_epoch_store.epoch_terminated().await;
4020
4021        // Record metrics in case the node has not observed epoch close in consensus.
4022        cur_epoch_store.record_epoch_close_time_once();
4023
4024        // Safe to begin reconfiguration now. No transactions are being executed,
4025        // and no epoch-specific tasks are running.
4026
4027        self.get_reconfig_api()
4028            .clear_state_end_of_epoch(&execution_lock);
4029        self.check_system_consistency(cur_epoch_store, state_hasher, expensive_safety_check_config);
4030        self.maybe_reaccumulate_state_hash(
4031            cur_epoch_store,
4032            epoch_start_configuration
4033                .epoch_start_state()
4034                .protocol_version(),
4035        );
4036        self.get_reconfig_api()
4037            .set_epoch_start_configuration(&epoch_start_configuration);
4038        if let Some(checkpoint_path) = &self.db_checkpoint_config.checkpoint_path
4039            && self
4040                .db_checkpoint_config
4041                .perform_db_checkpoints_at_epoch_end
4042        {
4043            let checkpoint_indexes = self
4044                .db_checkpoint_config
4045                .perform_index_db_checkpoints_at_epoch_end
4046                .unwrap_or(false);
4047            let current_epoch = cur_epoch_store.epoch();
4048            let epoch_checkpoint_path = checkpoint_path.join(format!("epoch_{}", current_epoch));
4049            self.checkpoint_all_dbs(&epoch_checkpoint_path, cur_epoch_store, checkpoint_indexes)?;
4050        }
4051
4052        self.get_reconfig_api()
4053            .reconfigure_cache(&epoch_start_configuration)
4054            .await;
4055
4056        let new_epoch = new_committee.epoch;
4057        let new_epoch_store = self
4058            .reopen_epoch_db(
4059                cur_epoch_store,
4060                new_committee,
4061                epoch_start_configuration,
4062                expensive_safety_check_config,
4063                epoch_last_checkpoint,
4064            )
4065            .await?;
4066        assert_eq!(new_epoch_store.epoch(), new_epoch);
4067        self.execution_scheduler
4068            .reconfigure(&new_epoch_store, self.get_account_funds_read());
4069        self.init_object_funds_checker().await;
4070
4071        // Update the committee and drop entries for peers no longer in it before tx
4072        // processing resumes for the new epoch.
4073        if let Err(e) = self
4074            .transaction_deny_config_manager
4075            .update_for_committee(new_epoch_store.committee().clone())
4076        {
4077            warn!(
4078                "TransactionDenyConfigManager update_for_committee failed at reconfigure: {:?}",
4079                e
4080            );
4081        }
4082
4083        *execution_lock = new_epoch;
4084
4085        self.notify_epoch(new_epoch);
4086        // drop execution_lock after epoch store was updated
4087        // see also assert in AuthorityState::process_certificate
4088        // on the epoch store and execution lock epoch match
4089        Ok(new_epoch_store)
4090    }
4091
4092    fn notify_epoch(&self, new_epoch: EpochId) {
4093        self.notify_epoch.send_modify(|epoch| *epoch = new_epoch);
4094    }
4095
4096    pub async fn wait_for_epoch(&self, target_epoch: EpochId) -> Result<EpochId, RecvError> {
4097        let mut rx = self.notify_epoch.subscribe();
4098        loop {
4099            let epoch = *rx.borrow();
4100            if epoch >= target_epoch {
4101                return Ok(epoch);
4102            }
4103            rx.changed().await?;
4104        }
4105    }
4106
4107    /// Executes accumulator settlement for testing purposes.
4108    /// Returns a list of (transaction, execution_env) pairs that can be replayed on another
4109    /// AuthorityState (e.g., a fullnode) using `replay_settlement_for_testing`.
4110    pub async fn settle_accumulator_for_testing(
4111        &self,
4112        effects: &[TransactionEffects],
4113        checkpoint_seq: Option<u64>,
4114    ) -> Vec<(VerifiedExecutableTransaction, ExecutionEnv)> {
4115        let accumulator_version = self
4116            .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
4117            .unwrap()
4118            .version();
4119        // Use provided checkpoint sequence, or fall back to accumulator version.
4120        let ckpt_seq = checkpoint_seq.unwrap_or_else(|| accumulator_version.value());
4121        let builder = AccumulatorSettlementTxBuilder::new(
4122            Some(self.get_transaction_cache_reader().as_ref()),
4123            effects,
4124            ckpt_seq,
4125            0,
4126        );
4127        let balance_changes = builder.collect_funds_changes();
4128        let epoch_store = self.epoch_store_for_testing();
4129        let epoch = epoch_store.epoch();
4130        let accumulator_root_obj_initial_shared_version = epoch_store
4131            .epoch_start_config()
4132            .accumulator_root_obj_initial_shared_version()
4133            .unwrap();
4134        let settlements = builder.build_tx(
4135            epoch_store.protocol_config(),
4136            epoch,
4137            accumulator_root_obj_initial_shared_version,
4138            ckpt_seq,
4139            ckpt_seq,
4140        );
4141
4142        let settlements: Vec<_> = settlements
4143            .into_iter()
4144            .map(|tx| {
4145                VerifiedExecutableTransaction::new_system(
4146                    VerifiedTransaction::new_system_transaction(tx),
4147                    epoch,
4148                )
4149            })
4150            .collect();
4151
4152        let assigned_versions = epoch_store
4153            .assign_shared_object_versions_for_tests(
4154                self.get_object_cache_reader().as_ref(),
4155                &settlements,
4156            )
4157            .unwrap();
4158        let version_map = assigned_versions.into_map();
4159
4160        let mut replay_txns = Vec::new();
4161        let mut settlement_effects = Vec::with_capacity(settlements.len());
4162        for tx in settlements {
4163            let assigned = version_map.get(&tx.key()).unwrap().clone();
4164            let env = ExecutionEnv::new().with_assigned_versions(assigned);
4165            let (effects, _) = self
4166                .try_execute_immediately(&tx.clone(), env.clone(), &epoch_store)
4167                .unwrap();
4168            assert!(effects.status().is_ok());
4169            replay_txns.push((tx, env));
4170            settlement_effects.push(effects);
4171        }
4172
4173        let barrier = accumulators::build_accumulator_barrier_tx(
4174            epoch,
4175            accumulator_root_obj_initial_shared_version,
4176            ckpt_seq,
4177            &settlement_effects,
4178        );
4179        let barrier = VerifiedExecutableTransaction::new_system(
4180            VerifiedTransaction::new_system_transaction(barrier),
4181            epoch,
4182        );
4183
4184        let assigned_versions = epoch_store
4185            .assign_shared_object_versions_for_tests(
4186                self.get_object_cache_reader().as_ref(),
4187                std::slice::from_ref(&barrier),
4188            )
4189            .unwrap();
4190        let version_map = assigned_versions.into_map();
4191
4192        let barrier_assigned = version_map.get(&barrier.key()).unwrap().clone();
4193        let env = ExecutionEnv::new().with_assigned_versions(barrier_assigned);
4194        let (effects, _) = self
4195            .try_execute_immediately(&barrier.clone(), env.clone(), &epoch_store)
4196            .unwrap();
4197        assert!(effects.status().is_ok());
4198        replay_txns.push((barrier, env));
4199
4200        let next_accumulator_version = accumulator_version.next();
4201        self.execution_scheduler
4202            .settle_address_funds(FundsSettlement {
4203                funds_changes: balance_changes,
4204                next_accumulator_version,
4205            });
4206        // object funds are settled while executing the barrier transaction
4207
4208        replay_txns
4209    }
4210
4211    /// Replays settlement transactions on this AuthorityState.
4212    /// Used to sync a fullnode with settlement transactions executed on a validator.
4213    pub async fn replay_settlement_for_testing(
4214        &self,
4215        txns: &[(VerifiedExecutableTransaction, ExecutionEnv)],
4216    ) {
4217        let epoch_store = self.epoch_store_for_testing();
4218        for (tx, env) in txns {
4219            let (effects, _) = self
4220                .try_execute_immediately(tx, env.clone(), &epoch_store)
4221                .unwrap();
4222            assert!(effects.status().is_ok());
4223        }
4224    }
4225
4226    /// Advance the epoch store to the next epoch for testing only.
4227    /// This only manually sets all the places where we have the epoch number.
4228    /// It doesn't properly reconfigure the node, hence should be only used for testing.
4229    pub async fn reconfigure_for_testing(&self) {
4230        let mut execution_lock = self.execution_lock_for_reconfiguration().await;
4231        let epoch_store = self.epoch_store_for_testing().clone();
4232        let protocol_config = epoch_store.protocol_config().clone();
4233        // The current protocol config used in the epoch store may have been overridden and diverged from
4234        // the protocol config definitions. That override may have now been dropped when the initial guard was dropped.
4235        // We reapply the override before creating the new epoch store, to make sure that
4236        // the new epoch store has the same protocol config as the current one.
4237        // Since this is for testing only, we mostly like to keep the protocol config the same
4238        // across epochs.
4239        let _guard =
4240            ProtocolConfig::apply_overrides_for_testing(move |_, _| protocol_config.clone());
4241        let new_epoch_store = epoch_store.new_at_next_epoch_for_testing(
4242            self.get_backing_package_store().clone(),
4243            self.get_object_store().clone(),
4244            &self.config.expensive_safety_check_config,
4245            self.checkpoint_store
4246                .get_epoch_last_checkpoint(epoch_store.epoch())
4247                .unwrap()
4248                .map(|c| *c.sequence_number())
4249                .unwrap_or_default(),
4250        );
4251        self.execution_scheduler
4252            .reconfigure(&new_epoch_store, self.get_account_funds_read());
4253        let new_epoch = new_epoch_store.epoch();
4254        self.epoch_store.store(new_epoch_store);
4255        epoch_store.epoch_terminated().await;
4256
4257        *execution_lock = new_epoch;
4258    }
4259
4260    /// This is a temporary method to be used when we enable simplified_unwrap_then_delete.
4261    /// It re-accumulates state hash for the new epoch if simplified_unwrap_then_delete is enabled.
4262    #[instrument(level = "error", skip_all)]
4263    fn maybe_reaccumulate_state_hash(
4264        &self,
4265        cur_epoch_store: &AuthorityPerEpochStore,
4266        new_protocol_version: ProtocolVersion,
4267    ) {
4268        self.get_reconfig_api()
4269            .maybe_reaccumulate_state_hash(cur_epoch_store, new_protocol_version);
4270    }
4271
4272    #[instrument(level = "error", skip_all)]
4273    fn check_system_consistency(
4274        &self,
4275        cur_epoch_store: &AuthorityPerEpochStore,
4276        state_hasher: Arc<GlobalStateHasher>,
4277        expensive_safety_check_config: &ExpensiveSafetyCheckConfig,
4278    ) {
4279        info!(
4280            "Performing sui conservation consistency check for epoch {}",
4281            cur_epoch_store.epoch()
4282        );
4283
4284        if let Err(err) = self
4285            .get_reconfig_api()
4286            .expensive_check_sui_conservation(cur_epoch_store)
4287        {
4288            if cfg!(debug_assertions) {
4289                panic!("{}", err);
4290            } else {
4291                // We cannot panic in production yet because it is known that there are some
4292                // inconsistencies in testnet. We will enable this once we make it balanced again in testnet.
4293                warn!("Sui conservation consistency check failed: {}", err);
4294            }
4295        } else {
4296            info!("Sui conservation consistency check passed");
4297        }
4298
4299        // check for root state hash consistency with live object set
4300        if expensive_safety_check_config.enable_state_consistency_check() {
4301            info!(
4302                "Performing state consistency check for epoch {}",
4303                cur_epoch_store.epoch()
4304            );
4305            self.expensive_check_is_consistent_state(state_hasher, cur_epoch_store);
4306        }
4307
4308        // Verify all checkpointed transactions are present in transactions_seq.
4309        // This catches any post-processing gaps that could occur if async
4310        // post-processing failed to complete before persistence.
4311        if expensive_safety_check_config.enable_secondary_index_checks()
4312            && let Some(indexes) = self.indexes.clone()
4313        {
4314            let epoch = cur_epoch_store.epoch();
4315            // Only verify the current epoch's checkpoints. Previous epoch contents
4316            // may have been pruned, and we only need to verify that this epoch's
4317            // async post-processing completed correctly.
4318            let first_checkpoint = if epoch == 0 {
4319                0
4320            } else {
4321                self.checkpoint_store
4322                    .get_epoch_last_checkpoint_seq_number(epoch - 1)
4323                    .expect("Failed to get previous epoch's last checkpoint")
4324                    .expect("Previous epoch's last checkpoint missing")
4325                    + 1
4326            };
4327            let highest_executed = self
4328                .checkpoint_store
4329                .get_highest_executed_checkpoint_seq_number()
4330                .expect("Failed to get highest executed checkpoint")
4331                .expect("No executed checkpoints");
4332
4333            info!(
4334                "Verifying checkpointed transactions are in transactions_seq \
4335                 (checkpoints {first_checkpoint}..={highest_executed})"
4336            );
4337            for seq in first_checkpoint..=highest_executed {
4338                let checkpoint = self
4339                    .checkpoint_store
4340                    .get_checkpoint_by_sequence_number(seq)
4341                    .expect("Failed to get checkpoint")
4342                    .expect("Checkpoint missing");
4343                let contents = self
4344                    .checkpoint_store
4345                    .get_checkpoint_contents(&checkpoint.content_digest)
4346                    .expect("Failed to get checkpoint contents")
4347                    .expect("Checkpoint contents missing");
4348                for digests in contents.iter() {
4349                    let tx_digest = digests.transaction;
4350                    assert!(
4351                        indexes
4352                            .get_transaction_seq(&tx_digest)
4353                            .expect("Failed to read transactions_seq")
4354                            .is_some(),
4355                        "Transaction {tx_digest} from checkpoint {seq} missing from transactions_seq"
4356                    );
4357                }
4358            }
4359            info!("All checkpointed transactions verified in transactions_seq");
4360        }
4361    }
4362
4363    fn expensive_check_is_consistent_state(
4364        &self,
4365        state_hasher: Arc<GlobalStateHasher>,
4366        cur_epoch_store: &AuthorityPerEpochStore,
4367    ) {
4368        let live_object_set_hash = state_hasher.digest_live_object_set(
4369            !cur_epoch_store
4370                .protocol_config()
4371                .simplified_unwrap_then_delete(),
4372        );
4373
4374        let root_state_hash: ECMHLiveObjectSetDigest = self
4375            .get_global_state_hash_store()
4376            .get_root_state_hash_for_epoch(cur_epoch_store.epoch())
4377            .expect("Retrieving root state hash cannot fail")
4378            .expect("Root state hash for epoch must exist")
4379            .1
4380            .digest()
4381            .into();
4382
4383        let is_inconsistent = root_state_hash != live_object_set_hash;
4384        if is_inconsistent {
4385            debug_fatal!(
4386                "Inconsistent state detected: root state hash: {:?}, live object set hash: {:?}",
4387                root_state_hash,
4388                live_object_set_hash
4389            );
4390        } else {
4391            info!("State consistency check passed");
4392        }
4393
4394        state_hasher.set_inconsistent_state(is_inconsistent);
4395    }
4396
4397    pub fn current_epoch_for_testing(&self) -> EpochId {
4398        self.epoch_store_for_testing().epoch()
4399    }
4400
4401    #[instrument(level = "error", skip_all)]
4402    pub fn checkpoint_all_dbs(
4403        &self,
4404        checkpoint_path: &Path,
4405        cur_epoch_store: &AuthorityPerEpochStore,
4406        checkpoint_indexes: bool,
4407    ) -> SuiResult {
4408        let _metrics_guard = self.metrics.db_checkpoint_latency.start_timer();
4409        let current_epoch = cur_epoch_store.epoch();
4410
4411        if checkpoint_path.exists() {
4412            info!("Skipping db checkpoint as it already exists for epoch: {current_epoch}");
4413            return Ok(());
4414        }
4415
4416        let checkpoint_path_tmp = checkpoint_path.with_extension("tmp");
4417        let store_checkpoint_path_tmp = checkpoint_path_tmp.join("store");
4418
4419        if checkpoint_path_tmp.exists() {
4420            fs::remove_dir_all(&checkpoint_path_tmp)
4421                .map_err(|e| SuiErrorKind::FileIOError(e.to_string()))?;
4422        }
4423
4424        fs::create_dir_all(&checkpoint_path_tmp)
4425            .map_err(|e| SuiErrorKind::FileIOError(e.to_string()))?;
4426        fs::create_dir(&store_checkpoint_path_tmp)
4427            .map_err(|e| SuiErrorKind::FileIOError(e.to_string()))?;
4428
4429        // NOTE: Do not change the order of invoking these checkpoint calls
4430        // We want to snapshot checkpoint db first to not race with state sync
4431        self.checkpoint_store
4432            .checkpoint_db(&checkpoint_path_tmp.join("checkpoints"))?;
4433
4434        self.get_reconfig_api()
4435            .checkpoint_db(&store_checkpoint_path_tmp.join("perpetual"))?;
4436
4437        self.committee_store
4438            .checkpoint_db(&checkpoint_path_tmp.join("epochs"))?;
4439
4440        if checkpoint_indexes && let Some(indexes) = self.indexes.as_ref() {
4441            indexes.checkpoint_db(&checkpoint_path_tmp.join("indexes"))?;
4442        }
4443
4444        fs::rename(checkpoint_path_tmp, checkpoint_path)
4445            .map_err(|e| SuiErrorKind::FileIOError(e.to_string()))?;
4446        Ok(())
4447    }
4448
4449    /// Load the current epoch store. This can change during reconfiguration. To ensure that
4450    /// we never end up accessing different epoch stores in a single task, we need to make sure
4451    /// that this is called once per task. Each call needs to be carefully audited to ensure it is
4452    /// the case. This also means we should minimize the number of call-sites. Only call it when
4453    /// there is no way to obtain it from somewhere else.
4454    pub fn load_epoch_store_one_call_per_task(&self) -> Guard<Arc<AuthorityPerEpochStore>> {
4455        self.epoch_store.load()
4456    }
4457
4458    // Load the epoch store, should be used in tests only.
4459    pub fn epoch_store_for_testing(&self) -> Guard<Arc<AuthorityPerEpochStore>> {
4460        self.load_epoch_store_one_call_per_task()
4461    }
4462
4463    pub fn transaction_deny_config_manager(&self) -> &Arc<TransactionDenyConfigManager> {
4464        &self.transaction_deny_config_manager
4465    }
4466
4467    /// The operator-configured local `TransactionDenyConfig` (before any peer
4468    /// recommendations are merged).
4469    pub fn local_transaction_deny_config(&self) -> &Arc<TransactionDenyConfig> {
4470        self.transaction_deny_config_manager.local()
4471    }
4472
4473    pub fn clone_committee_for_testing(&self) -> Committee {
4474        Committee::clone(self.epoch_store_for_testing().committee())
4475    }
4476
4477    #[instrument(level = "trace", skip_all)]
4478    pub fn get_object(&self, object_id: &ObjectID) -> Option<Object> {
4479        self.get_object_store().get_object(object_id)
4480    }
4481
4482    pub fn get_sui_system_package_object_ref(&self) -> SuiResult<ObjectRef> {
4483        Ok(self
4484            .get_object(&SUI_SYSTEM_ADDRESS.into())
4485            .expect("framework object should always exist")
4486            .compute_object_reference())
4487    }
4488
4489    // This function is only used for testing.
4490    pub fn get_sui_system_state_object_for_testing(&self) -> SuiResult<SuiSystemState> {
4491        self.get_object_cache_reader()
4492            .get_sui_system_state_object_unsafe()
4493    }
4494
4495    #[instrument(level = "trace", skip_all)]
4496    fn get_transaction_checkpoint_sequence(
4497        &self,
4498        digest: &TransactionDigest,
4499        epoch_store: &AuthorityPerEpochStore,
4500    ) -> SuiResult<Option<CheckpointSequenceNumber>> {
4501        epoch_store.get_transaction_checkpoint(digest)
4502    }
4503
4504    #[instrument(level = "trace", skip_all)]
4505    pub fn get_checkpoint_by_sequence_number(
4506        &self,
4507        sequence_number: CheckpointSequenceNumber,
4508    ) -> SuiResult<Option<VerifiedCheckpoint>> {
4509        Ok(self
4510            .checkpoint_store
4511            .get_checkpoint_by_sequence_number(sequence_number)?)
4512    }
4513
4514    #[instrument(level = "trace", skip_all)]
4515    pub fn get_transaction_checkpoint_for_tests(
4516        &self,
4517        digest: &TransactionDigest,
4518        epoch_store: &AuthorityPerEpochStore,
4519    ) -> SuiResult<Option<VerifiedCheckpoint>> {
4520        let checkpoint = self.get_transaction_checkpoint_sequence(digest, epoch_store)?;
4521        let Some(checkpoint) = checkpoint else {
4522            return Ok(None);
4523        };
4524        let checkpoint = self
4525            .checkpoint_store
4526            .get_checkpoint_by_sequence_number(checkpoint)?;
4527        Ok(checkpoint)
4528    }
4529
4530    #[instrument(level = "trace", skip_all)]
4531    pub fn get_object_read(&self, object_id: &ObjectID) -> SuiResult<ObjectRead> {
4532        Ok(
4533            match self
4534                .get_object_cache_reader()
4535                .get_latest_object_or_tombstone(*object_id)
4536            {
4537                Some((_, ObjectOrTombstone::Object(object))) => {
4538                    let layout = self.get_object_layout(&object)?;
4539                    ObjectRead::Exists(object.compute_object_reference(), object, layout)
4540                }
4541                Some((_, ObjectOrTombstone::Tombstone(objref))) => ObjectRead::Deleted(objref),
4542                None => ObjectRead::NotExists(*object_id),
4543            },
4544        )
4545    }
4546
4547    /// Chain Identifier is the digest of the genesis checkpoint.
4548    pub fn get_chain_identifier(&self) -> ChainIdentifier {
4549        self.chain_identifier
4550    }
4551
4552    /// This function aims to serve rpc reads on past objects and
4553    /// we don't expect it to be called for other purposes.
4554    /// Depending on the object pruning policies that will be enforced in the
4555    /// future there is no software-level guarantee/SLA to retrieve an object
4556    /// with an old version even if it exists/existed.
4557    #[instrument(level = "trace", skip_all)]
4558    pub fn get_past_object_read(
4559        &self,
4560        object_id: &ObjectID,
4561        version: SequenceNumber,
4562    ) -> SuiResult<PastObjectRead> {
4563        // Firstly we see if the object ever existed by getting its latest data
4564        let Some(obj_ref) = self
4565            .get_object_cache_reader()
4566            .get_latest_object_ref_or_tombstone(*object_id)
4567        else {
4568            return Ok(PastObjectRead::ObjectNotExists(*object_id));
4569        };
4570
4571        if version > obj_ref.1 {
4572            return Ok(PastObjectRead::VersionTooHigh {
4573                object_id: *object_id,
4574                asked_version: version,
4575                latest_version: obj_ref.1,
4576            });
4577        }
4578
4579        if version < obj_ref.1 {
4580            // Read past objects
4581            return Ok(match self.read_object_at_version(object_id, version)? {
4582                Some((object, layout)) => {
4583                    let obj_ref = object.compute_object_reference();
4584                    PastObjectRead::VersionFound(obj_ref, object, layout)
4585                }
4586
4587                None => PastObjectRead::VersionNotFound(*object_id, version),
4588            });
4589        }
4590
4591        if !obj_ref.2.is_alive() {
4592            return Ok(PastObjectRead::ObjectDeleted(obj_ref));
4593        }
4594
4595        match self.read_object_at_version(object_id, obj_ref.1)? {
4596            Some((object, layout)) => Ok(PastObjectRead::VersionFound(obj_ref, object, layout)),
4597            None => {
4598                debug_fatal!(
4599                    "Object with in parent_entry is missing from object store, datastore is \
4600                     inconsistent"
4601                );
4602                Err(UserInputError::ObjectNotFound {
4603                    object_id: *object_id,
4604                    version: Some(obj_ref.1),
4605                }
4606                .into())
4607            }
4608        }
4609    }
4610
4611    #[instrument(level = "trace", skip_all)]
4612    fn read_object_at_version(
4613        &self,
4614        object_id: &ObjectID,
4615        version: SequenceNumber,
4616    ) -> SuiResult<Option<(Object, Option<MoveStructLayout>)>> {
4617        let Some(object) = self
4618            .get_object_cache_reader()
4619            .get_object_by_key(object_id, version)
4620        else {
4621            return Ok(None);
4622        };
4623
4624        let layout = self.get_object_layout(&object)?;
4625        Ok(Some((object, layout)))
4626    }
4627
4628    pub fn get_object_layout(&self, object: &Object) -> SuiResult<Option<MoveStructLayout>> {
4629        let layout = object
4630            .data
4631            .try_as_move()
4632            .map(|object| {
4633                let epoch_store = self.load_epoch_store_one_call_per_task();
4634                into_struct_layout(
4635                    epoch_store
4636                        .executor()
4637                        // TODO(cache) - must read through cache
4638                        .type_layout_resolver(
4639                            epoch_store.protocol_config(),
4640                            Box::new(self.get_backing_package_store().as_ref()),
4641                        )
4642                        .get_annotated_layout(&object.type_().clone().into())?,
4643                )
4644            })
4645            .transpose()?;
4646        Ok(layout)
4647    }
4648
4649    /// Returns a fake ObjectRef representing an address balance, along with the balance value
4650    /// and the previous transaction digest. The ObjectRef can be returned to JSON-RPC clients
4651    /// that don't understand address balances.
4652    #[instrument(level = "trace", skip_all)]
4653    pub fn get_address_balance_coin_info(
4654        &self,
4655        owner: SuiAddress,
4656        balance_type: TypeTag,
4657    ) -> SuiResult<Option<(ObjectRef, u64, TransactionDigest)>> {
4658        let accumulator_id = AccumulatorValue::get_field_id(owner, &balance_type)?;
4659        let accumulator_obj = AccumulatorValue::load_object_by_id(
4660            self.get_runtime_object_resolver().as_ref(),
4661            None,
4662            *accumulator_id.inner(),
4663        )?;
4664
4665        let Some(accumulator_obj) = accumulator_obj else {
4666            return Ok(None);
4667        };
4668
4669        // Extract the currency type from balance_type (e.g., SUI from Balance<SUI>).
4670        // get_balance expects the currency type, not the balance type.
4671        let currency_type =
4672            Balance::maybe_get_balance_type_param(&balance_type).unwrap_or(balance_type);
4673
4674        let balance = crate::accumulators::balances::get_balance(
4675            owner,
4676            self.get_runtime_object_resolver().as_ref(),
4677            currency_type,
4678        )?;
4679
4680        if balance == 0 {
4681            return Ok(None);
4682        };
4683
4684        let object_ref = coin_reservation::encode_object_ref(
4685            accumulator_obj.id(),
4686            accumulator_obj.version(),
4687            self.load_epoch_store_one_call_per_task().epoch(),
4688            balance,
4689            self.get_chain_identifier(),
4690        );
4691
4692        Ok(Some((
4693            object_ref,
4694            balance,
4695            accumulator_obj.previous_transaction,
4696        )))
4697    }
4698
4699    /// Returns fake ObjectRefs for all address balances of an owner, keyed by coin type string.
4700    /// Used by get_all_coins to include fake coins for each coin type.
4701    #[instrument(level = "trace", skip_all)]
4702    pub fn get_all_address_balance_coin_infos(
4703        &self,
4704        owner: SuiAddress,
4705    ) -> SuiResult<std::collections::HashMap<String, (ObjectRef, u64, TransactionDigest)>> {
4706        let indexes = self
4707            .indexes
4708            .as_ref()
4709            .ok_or(SuiErrorKind::IndexStoreNotAvailable)?;
4710
4711        let mut result = std::collections::HashMap::new();
4712        for currency_type in indexes.get_address_balance_coin_types_iter(owner) {
4713            let balance_type = sui_types::balance::Balance::type_tag(currency_type.clone());
4714            if let Some((obj_ref, balance, prev_tx)) =
4715                self.get_address_balance_coin_info(owner, balance_type)?
4716            {
4717                // Use currency_type.to_string() to match the format in CoinIndexKey2
4718                // (e.g., "0x2::sui::SUI", not "0x2::coin::Coin<0x2::sui::SUI>")
4719                result.insert(currency_type.to_string(), (obj_ref, balance, prev_tx));
4720            }
4721        }
4722        Ok(result)
4723    }
4724
4725    fn get_owner_at_version(
4726        object_store: &Arc<dyn ObjectStore + Send + Sync>,
4727        object_id: &ObjectID,
4728        version: SequenceNumber,
4729    ) -> SuiResult<Owner> {
4730        object_store
4731            .get_object_by_key(object_id, version)
4732            .ok_or_else(|| {
4733                SuiError::from(UserInputError::ObjectNotFound {
4734                    object_id: *object_id,
4735                    version: Some(version),
4736                })
4737            })
4738            .map(|o| o.owner.clone())
4739    }
4740
4741    #[instrument(level = "trace", skip_all)]
4742    pub fn get_owner_objects(
4743        &self,
4744        owner: SuiAddress,
4745        // If `Some`, the query will start from the next item after the specified cursor
4746        cursor: Option<ObjectID>,
4747        limit: usize,
4748        filter: Option<SuiObjectDataFilter>,
4749    ) -> SuiResult<Vec<ObjectInfo>> {
4750        if let Some(indexes) = &self.indexes {
4751            indexes.get_owner_objects(owner, cursor, limit, filter)
4752        } else {
4753            Err(SuiErrorKind::IndexStoreNotAvailable.into())
4754        }
4755    }
4756
4757    #[instrument(level = "trace", skip_all)]
4758    pub fn get_owned_coins_iterator_with_cursor(
4759        &self,
4760        owner: SuiAddress,
4761        // If `Some`, the query will start from the next item after the specified cursor
4762        cursor: (String, u64, ObjectID),
4763        limit: usize,
4764        one_coin_type_only: bool,
4765    ) -> SuiResult<impl Iterator<Item = (CoinIndexKey2, CoinInfo)> + '_> {
4766        if let Some(indexes) = &self.indexes {
4767            indexes.get_owned_coins_iterator_with_cursor(owner, cursor, limit, one_coin_type_only)
4768        } else {
4769            Err(SuiErrorKind::IndexStoreNotAvailable.into())
4770        }
4771    }
4772
4773    #[instrument(level = "trace", skip_all)]
4774    pub fn get_owner_objects_iterator(
4775        &self,
4776        owner: SuiAddress,
4777        // If `Some`, the query will start from the next item after the specified cursor
4778        cursor: Option<ObjectID>,
4779        filter: Option<SuiObjectDataFilter>,
4780    ) -> SuiResult<impl Iterator<Item = ObjectInfo> + '_> {
4781        let cursor_u = cursor.unwrap_or(ObjectID::ZERO);
4782        if let Some(indexes) = &self.indexes {
4783            indexes.get_owner_objects_iterator(owner, cursor_u, filter)
4784        } else {
4785            Err(SuiErrorKind::IndexStoreNotAvailable.into())
4786        }
4787    }
4788
4789    #[instrument(level = "trace", skip_all)]
4790    pub fn get_move_objects<T>(&self, owner: SuiAddress, type_: MoveObjectType) -> SuiResult<Vec<T>>
4791    where
4792        T: DeserializeOwned,
4793    {
4794        let object_ids = self
4795            .get_owner_objects_iterator(owner, None, None)?
4796            .filter(|o| match &o.type_ {
4797                ObjectType::Struct(s) => &type_ == s,
4798                ObjectType::Package => false,
4799            })
4800            .map(|info| ObjectKey(info.object_id, info.version))
4801            .collect::<Vec<_>>();
4802        let mut move_objects = vec![];
4803
4804        let objects = self
4805            .get_object_store()
4806            .multi_get_objects_by_key(&object_ids);
4807
4808        for (o, id) in objects.into_iter().zip_debug_eq(object_ids) {
4809            let object = o.ok_or_else(|| {
4810                SuiError::from(UserInputError::ObjectNotFound {
4811                    object_id: id.0,
4812                    version: Some(id.1),
4813                })
4814            })?;
4815            let move_object = object.data.try_as_move().ok_or_else(|| {
4816                SuiError::from(UserInputError::MovePackageAsObject { object_id: id.0 })
4817            })?;
4818            move_objects.push(bcs::from_bytes(move_object.contents()).map_err(|e| {
4819                SuiErrorKind::ObjectDeserializationError {
4820                    error: format!("{e}"),
4821                }
4822            })?);
4823        }
4824        Ok(move_objects)
4825    }
4826
4827    #[instrument(level = "trace", skip_all)]
4828    pub fn get_dynamic_fields(
4829        &self,
4830        owner: ObjectID,
4831        // If `Some`, the query will start from the next item after the specified cursor
4832        cursor: Option<ObjectID>,
4833        limit: usize,
4834    ) -> SuiResult<Vec<(ObjectID, DynamicFieldInfo)>> {
4835        Ok(self
4836            .get_dynamic_fields_iterator(owner, cursor)?
4837            .take(limit)
4838            .collect::<Result<Vec<_>, _>>()?)
4839    }
4840
4841    fn get_dynamic_fields_iterator(
4842        &self,
4843        owner: ObjectID,
4844        // If `Some`, the query will start from the next item after the specified cursor
4845        cursor: Option<ObjectID>,
4846    ) -> SuiResult<impl Iterator<Item = Result<(ObjectID, DynamicFieldInfo), TypedStoreError>> + '_>
4847    {
4848        if let Some(indexes) = &self.indexes {
4849            indexes.get_dynamic_fields_iterator(owner, cursor)
4850        } else {
4851            Err(SuiErrorKind::IndexStoreNotAvailable.into())
4852        }
4853    }
4854
4855    #[instrument(level = "trace", skip_all)]
4856    pub fn get_dynamic_field_object_id(
4857        &self,
4858        owner: ObjectID,
4859        name_type: TypeTag,
4860        name_bcs_bytes: &[u8],
4861    ) -> SuiResult<Option<ObjectID>> {
4862        if let Some(indexes) = &self.indexes {
4863            indexes.get_dynamic_field_object_id(owner, name_type, name_bcs_bytes)
4864        } else {
4865            Err(SuiErrorKind::IndexStoreNotAvailable.into())
4866        }
4867    }
4868
4869    #[instrument(level = "trace", skip_all)]
4870    pub fn get_total_transaction_blocks(&self) -> SuiResult<u64> {
4871        Ok(self.get_indexes()?.next_sequence_number())
4872    }
4873
4874    #[instrument(level = "trace", skip_all)]
4875    pub async fn get_executed_transaction_and_effects(
4876        &self,
4877        digest: TransactionDigest,
4878        kv_store: Arc<TransactionKeyValueStore>,
4879    ) -> SuiResult<(Transaction, TransactionEffects)> {
4880        let transaction = kv_store.get_tx(digest).await?;
4881        let effects = kv_store.get_fx_by_tx_digest(digest).await?;
4882        Ok((transaction, effects))
4883    }
4884
4885    #[instrument(level = "trace", skip_all)]
4886    pub fn multi_get_checkpoint_by_sequence_number(
4887        &self,
4888        sequence_numbers: &[CheckpointSequenceNumber],
4889    ) -> SuiResult<Vec<Option<VerifiedCheckpoint>>> {
4890        Ok(self
4891            .checkpoint_store
4892            .multi_get_checkpoint_by_sequence_number(sequence_numbers)?)
4893    }
4894
4895    #[instrument(level = "trace", skip_all)]
4896    pub fn get_transaction_events(
4897        &self,
4898        digest: &TransactionDigest,
4899    ) -> SuiResult<TransactionEvents> {
4900        self.get_transaction_cache_reader()
4901            .get_events(digest)
4902            .ok_or(SuiErrorKind::TransactionEventsNotFound { digest: *digest }.into())
4903    }
4904
4905    pub fn get_transaction_input_objects(
4906        &self,
4907        effects: &TransactionEffects,
4908    ) -> SuiResult<Vec<Object>> {
4909        sui_types::storage::get_transaction_input_objects(self.get_object_store(), effects)
4910            .map_err(Into::into)
4911    }
4912
4913    pub fn get_transaction_output_objects(
4914        &self,
4915        effects: &TransactionEffects,
4916    ) -> SuiResult<Vec<Object>> {
4917        sui_types::storage::get_transaction_output_objects(self.get_object_store(), effects)
4918            .map_err(Into::into)
4919    }
4920
4921    fn get_indexes(&self) -> SuiResult<Arc<IndexStore>> {
4922        match &self.indexes {
4923            Some(i) => Ok(i.clone()),
4924            None => Err(SuiErrorKind::UnsupportedFeatureError {
4925                error: "extended object indexing is not enabled on this server".into(),
4926            }
4927            .into()),
4928        }
4929    }
4930
4931    pub async fn get_transactions_for_tests(
4932        self: &Arc<Self>,
4933        filter: Option<TransactionFilter>,
4934        cursor: Option<TransactionDigest>,
4935        limit: Option<usize>,
4936        reverse: bool,
4937    ) -> SuiResult<Vec<TransactionDigest>> {
4938        let metrics = KeyValueStoreMetrics::new_for_tests();
4939        let kv_store = Arc::new(TransactionKeyValueStore::new(
4940            "rocksdb",
4941            metrics,
4942            self.clone(),
4943        ));
4944        self.get_transactions(&kv_store, filter, cursor, limit, reverse)
4945            .await
4946    }
4947
4948    #[instrument(level = "trace", skip_all)]
4949    pub async fn get_transactions(
4950        &self,
4951        kv_store: &Arc<TransactionKeyValueStore>,
4952        filter: Option<TransactionFilter>,
4953        // If `Some`, the query will start from the next item after the specified cursor
4954        cursor: Option<TransactionDigest>,
4955        limit: Option<usize>,
4956        reverse: bool,
4957    ) -> SuiResult<Vec<TransactionDigest>> {
4958        if let Some(TransactionFilter::Checkpoint(sequence_number)) = filter {
4959            let checkpoint_contents = kv_store.get_checkpoint_contents(sequence_number).await?;
4960            let iter = checkpoint_contents.iter().map(|c| c.transaction);
4961            if reverse {
4962                let iter = iter
4963                    .rev()
4964                    .skip_while(|d| cursor.is_some() && Some(*d) != cursor)
4965                    .skip(usize::from(cursor.is_some()));
4966                return Ok(iter.take(limit.unwrap_or(usize::MAX)).collect());
4967            } else {
4968                let iter = iter
4969                    .skip_while(|d| cursor.is_some() && Some(*d) != cursor)
4970                    .skip(usize::from(cursor.is_some()));
4971                return Ok(iter.take(limit.unwrap_or(usize::MAX)).collect());
4972            }
4973        }
4974        self.get_indexes()?
4975            .get_transactions(filter, cursor, limit, reverse)
4976    }
4977
4978    pub fn get_checkpoint_store(&self) -> &Arc<CheckpointStore> {
4979        &self.checkpoint_store
4980    }
4981
4982    pub fn get_latest_checkpoint_sequence_number(&self) -> SuiResult<CheckpointSequenceNumber> {
4983        self.get_checkpoint_store()
4984            .get_highest_executed_checkpoint_seq_number()?
4985            .ok_or(
4986                SuiErrorKind::UserInputError {
4987                    error: UserInputError::LatestCheckpointSequenceNumberNotFound,
4988                }
4989                .into(),
4990            )
4991    }
4992
4993    #[cfg(msim)]
4994    pub fn get_highest_pruned_checkpoint_for_testing(&self) -> SuiResult<CheckpointSequenceNumber> {
4995        self.database_for_testing()
4996            .perpetual_tables
4997            .get_highest_pruned_checkpoint()
4998            .map(|c| c.unwrap_or(0))
4999            .map_err(Into::into)
5000    }
5001
5002    #[instrument(level = "trace", skip_all)]
5003    pub fn get_checkpoint_summary_by_sequence_number(
5004        &self,
5005        sequence_number: CheckpointSequenceNumber,
5006    ) -> SuiResult<CheckpointSummary> {
5007        let verified_checkpoint = self
5008            .get_checkpoint_store()
5009            .get_checkpoint_by_sequence_number(sequence_number)?;
5010        match verified_checkpoint {
5011            Some(verified_checkpoint) => Ok(verified_checkpoint.into_inner().into_data()),
5012            None => Err(SuiErrorKind::UserInputError {
5013                error: UserInputError::VerifiedCheckpointNotFound(sequence_number),
5014            }
5015            .into()),
5016        }
5017    }
5018
5019    #[instrument(level = "trace", skip_all)]
5020    pub fn get_checkpoint_summary_by_digest(
5021        &self,
5022        digest: CheckpointDigest,
5023    ) -> SuiResult<CheckpointSummary> {
5024        let verified_checkpoint = self
5025            .get_checkpoint_store()
5026            .get_checkpoint_by_digest(&digest)?;
5027        match verified_checkpoint {
5028            Some(verified_checkpoint) => Ok(verified_checkpoint.into_inner().into_data()),
5029            None => Err(SuiErrorKind::UserInputError {
5030                error: UserInputError::VerifiedCheckpointDigestNotFound(Base58::encode(digest)),
5031            }
5032            .into()),
5033        }
5034    }
5035
5036    #[instrument(level = "trace", skip_all)]
5037    pub fn find_publish_txn_digest(&self, package_id: ObjectID) -> SuiResult<TransactionDigest> {
5038        if is_system_package(package_id) {
5039            return self.find_genesis_txn_digest();
5040        }
5041        Ok(self
5042            .get_object_read(&package_id)?
5043            .into_object()?
5044            .previous_transaction)
5045    }
5046
5047    #[instrument(level = "trace", skip_all)]
5048    pub fn find_genesis_txn_digest(&self) -> SuiResult<TransactionDigest> {
5049        let summary = self
5050            .get_verified_checkpoint_by_sequence_number(0)?
5051            .into_message();
5052        let content = self.get_checkpoint_contents(summary.content_digest)?;
5053        let genesis_transaction = content.enumerate_transactions(&summary).next();
5054        Ok(genesis_transaction
5055            .ok_or(SuiErrorKind::UserInputError {
5056                error: UserInputError::GenesisTransactionNotFound,
5057            })?
5058            .1
5059            .transaction)
5060    }
5061
5062    #[instrument(level = "trace", skip_all)]
5063    pub fn get_verified_checkpoint_by_sequence_number(
5064        &self,
5065        sequence_number: CheckpointSequenceNumber,
5066    ) -> SuiResult<VerifiedCheckpoint> {
5067        let verified_checkpoint = self
5068            .get_checkpoint_store()
5069            .get_checkpoint_by_sequence_number(sequence_number)?;
5070        match verified_checkpoint {
5071            Some(verified_checkpoint) => Ok(verified_checkpoint),
5072            None => Err(SuiErrorKind::UserInputError {
5073                error: UserInputError::VerifiedCheckpointNotFound(sequence_number),
5074            }
5075            .into()),
5076        }
5077    }
5078
5079    #[instrument(level = "trace", skip_all)]
5080    pub fn get_verified_checkpoint_summary_by_digest(
5081        &self,
5082        digest: CheckpointDigest,
5083    ) -> SuiResult<VerifiedCheckpoint> {
5084        let verified_checkpoint = self
5085            .get_checkpoint_store()
5086            .get_checkpoint_by_digest(&digest)?;
5087        match verified_checkpoint {
5088            Some(verified_checkpoint) => Ok(verified_checkpoint),
5089            None => Err(SuiErrorKind::UserInputError {
5090                error: UserInputError::VerifiedCheckpointDigestNotFound(Base58::encode(digest)),
5091            }
5092            .into()),
5093        }
5094    }
5095
5096    #[instrument(level = "trace", skip_all)]
5097    pub fn get_checkpoint_contents(
5098        &self,
5099        digest: CheckpointContentsDigest,
5100    ) -> SuiResult<CheckpointContents> {
5101        self.get_checkpoint_store()
5102            .get_checkpoint_contents(&digest)?
5103            .ok_or(
5104                SuiErrorKind::UserInputError {
5105                    error: UserInputError::CheckpointContentsNotFound(digest),
5106                }
5107                .into(),
5108            )
5109    }
5110
5111    #[instrument(level = "trace", skip_all)]
5112    pub fn get_checkpoint_contents_by_sequence_number(
5113        &self,
5114        sequence_number: CheckpointSequenceNumber,
5115    ) -> SuiResult<CheckpointContents> {
5116        let verified_checkpoint = self
5117            .get_checkpoint_store()
5118            .get_checkpoint_by_sequence_number(sequence_number)?;
5119        match verified_checkpoint {
5120            Some(verified_checkpoint) => {
5121                let content_digest = verified_checkpoint.into_inner().content_digest;
5122                self.get_checkpoint_contents(content_digest)
5123            }
5124            None => Err(SuiErrorKind::UserInputError {
5125                error: UserInputError::VerifiedCheckpointNotFound(sequence_number),
5126            }
5127            .into()),
5128        }
5129    }
5130
5131    #[instrument(level = "trace", skip_all)]
5132    pub async fn query_events(
5133        &self,
5134        kv_store: &Arc<TransactionKeyValueStore>,
5135        query: EventFilter,
5136        // If `Some`, the query will start from the next item after the specified cursor
5137        cursor: Option<EventID>,
5138        limit: usize,
5139        descending: bool,
5140    ) -> SuiResult<Vec<SuiEvent>> {
5141        let index_store = self.get_indexes()?;
5142
5143        //Get the tx_num from tx_digest
5144        let (tx_num, event_num) = if let Some(cursor) = cursor.as_ref() {
5145            let tx_seq = index_store.get_transaction_seq(&cursor.tx_digest)?.ok_or(
5146                SuiErrorKind::TransactionNotFound {
5147                    digest: cursor.tx_digest,
5148                },
5149            )?;
5150            (tx_seq, cursor.event_seq as usize)
5151        } else if descending {
5152            (u64::MAX, usize::MAX)
5153        } else {
5154            (0, 0)
5155        };
5156
5157        let limit = limit + 1;
5158        let mut event_keys = match query {
5159            EventFilter::All([]) => index_store.all_events(tx_num, event_num, limit, descending)?,
5160            EventFilter::Transaction(digest) => {
5161                index_store.events_by_transaction(&digest, tx_num, event_num, limit, descending)?
5162            }
5163            EventFilter::MoveModule { package, module } => {
5164                let module_id = ModuleId::new(package.into(), module);
5165                index_store.events_by_module_id(&module_id, tx_num, event_num, limit, descending)?
5166            }
5167            EventFilter::MoveEventType(struct_name) => index_store
5168                .events_by_move_event_struct_name(
5169                    &struct_name,
5170                    tx_num,
5171                    event_num,
5172                    limit,
5173                    descending,
5174                )?,
5175            EventFilter::Sender(sender) => {
5176                index_store.events_by_sender(&sender, tx_num, event_num, limit, descending)?
5177            }
5178            EventFilter::TimeRange {
5179                start_time,
5180                end_time,
5181            } => index_store
5182                .event_iterator(start_time, end_time, tx_num, event_num, limit, descending)?,
5183            EventFilter::MoveEventModule { package, module } => index_store
5184                .events_by_move_event_module(
5185                    &ModuleId::new(package.into(), module),
5186                    tx_num,
5187                    event_num,
5188                    limit,
5189                    descending,
5190                )?,
5191            // not using "_ =>" because we want to make sure we remember to add new variants here
5192            EventFilter::Any(_) => {
5193                return Err(SuiErrorKind::UserInputError {
5194                    error: UserInputError::Unsupported(
5195                        "'Any' queries are not supported by the fullnode.".to_string(),
5196                    ),
5197                }
5198                .into());
5199            }
5200        };
5201
5202        // skip one event if exclusive cursor is provided,
5203        // otherwise truncate to the original limit.
5204        if cursor.is_some() {
5205            if !event_keys.is_empty() {
5206                event_keys.remove(0);
5207            }
5208        } else {
5209            event_keys.truncate(limit - 1);
5210        }
5211
5212        // get the unique set of digests from the event_keys
5213        let transaction_digests = event_keys
5214            .iter()
5215            .map(|(_, digest, _, _)| *digest)
5216            .collect::<HashSet<_>>()
5217            .into_iter()
5218            .collect::<Vec<_>>();
5219
5220        let events = kv_store
5221            .multi_get_events_by_tx_digests(&transaction_digests)
5222            .await?;
5223
5224        let events_map: HashMap<_, _> = transaction_digests
5225            .iter()
5226            .zip_debug_eq(events.into_iter())
5227            .collect();
5228
5229        let stored_events = event_keys
5230            .into_iter()
5231            .map(|k| {
5232                (
5233                    k,
5234                    events_map
5235                        .get(&k.1)
5236                        .expect("fetched digest is missing")
5237                        .clone()
5238                        .and_then(|e| e.data.get(k.2).cloned()),
5239                )
5240            })
5241            .map(
5242                |((_event_digest, tx_digest, event_seq, timestamp), event)| {
5243                    event
5244                        .map(|e| (e, tx_digest, event_seq, timestamp))
5245                        .ok_or_else(|| {
5246                            SuiError::from(SuiErrorKind::TransactionEventsNotFound {
5247                                digest: tx_digest,
5248                            })
5249                        })
5250                },
5251            )
5252            .collect::<Result<Vec<_>, _>>()?;
5253
5254        let epoch_store = self.load_epoch_store_one_call_per_task();
5255        let backing_store = self.get_backing_package_store().as_ref();
5256        let mut layout_resolver = epoch_store
5257            .executor()
5258            .type_layout_resolver(epoch_store.protocol_config(), Box::new(backing_store));
5259        let mut events = vec![];
5260        for (e, tx_digest, event_seq, timestamp) in stored_events.into_iter() {
5261            events.push(SuiEvent::try_from(
5262                e.clone(),
5263                tx_digest,
5264                event_seq as u64,
5265                Some(timestamp),
5266                layout_resolver.get_annotated_layout(&e.type_)?,
5267            )?)
5268        }
5269        Ok(events)
5270    }
5271
5272    pub fn insert_genesis_object(&self, object: Object) {
5273        self.get_reconfig_api().insert_genesis_object(object);
5274    }
5275
5276    pub fn insert_genesis_objects(&self, objects: &[Object]) {
5277        for o in objects {
5278            self.insert_genesis_object(o.clone());
5279        }
5280    }
5281
5282    /// Make a status response for a transaction
5283    #[instrument(level = "trace", skip_all)]
5284    pub fn get_transaction_status(
5285        &self,
5286        transaction_digest: &TransactionDigest,
5287        epoch_store: &Arc<AuthorityPerEpochStore>,
5288    ) -> SuiResult<Option<(SenderSignedData, TransactionStatus)>> {
5289        // TODO: In the case of read path, we should not have to re-sign the effects.
5290        if let Some(effects) =
5291            self.get_signed_effects_and_maybe_resign(transaction_digest, epoch_store)?
5292        {
5293            if let Some(transaction) = self
5294                .get_transaction_cache_reader()
5295                .get_transaction_block(transaction_digest)
5296            {
5297                let events = if effects.events_digest().is_some() {
5298                    self.get_transaction_events(effects.transaction_digest())?
5299                } else {
5300                    TransactionEvents::default()
5301                };
5302                // The cert_sig slot is permanently None: validators no longer aggregate or
5303                // persist per-transaction quorum signatures.
5304                return Ok(Some((
5305                    (*transaction).clone().into_message(),
5306                    TransactionStatus::Executed(None, effects.into_inner(), events),
5307                )));
5308            } else {
5309                // The read of effects and read of transaction are not atomic. It's possible that we reverted
5310                // the transaction (during epoch change) in between the above two reads, and we end up
5311                // having effects but not transaction. In this case, we just fall through.
5312                debug!(tx_digest=?transaction_digest, "Signed effects exist but no transaction found");
5313            }
5314        }
5315        // Validators no longer sign transactions before execution, so there is no
5316        // TransactionStatus::Signed state to report.
5317        Ok(None)
5318    }
5319
5320    /// Get the signed effects of the given transaction. If the effects was signed in a previous
5321    /// epoch, re-sign it so that the caller is able to form a cert of the effects in the current
5322    /// epoch.
5323    #[instrument(level = "trace", skip_all)]
5324    pub fn get_signed_effects_and_maybe_resign(
5325        &self,
5326        transaction_digest: &TransactionDigest,
5327        epoch_store: &Arc<AuthorityPerEpochStore>,
5328    ) -> SuiResult<Option<VerifiedSignedTransactionEffects>> {
5329        let effects = self
5330            .get_transaction_cache_reader()
5331            .get_executed_effects(transaction_digest);
5332        match effects {
5333            Some(effects) => {
5334                // If the transaction was executed in previous epochs, the validator will
5335                // re-sign the effects with new current epoch so that a client is always able to
5336                // obtain an effects certificate at the current epoch.
5337                //
5338                // Why is this necessary? Consider the following case:
5339                // - assume there are 4 validators
5340                // - Quorum driver gets 2 signed effects before reconfig halt
5341                // - The tx makes it into final checkpoint.
5342                // - 2 validators go away and are replaced in the new epoch.
5343                // - The new epoch begins.
5344                // - The quorum driver cannot complete the partial effects cert from the previous epoch,
5345                //   because it may not be able to reach either of the 2 former validators.
5346                // - But, if the 2 validators that stayed are willing to re-sign the effects in the new
5347                //   epoch, the QD can make a new effects cert and return it to the client.
5348                //
5349                // This is a considered a short-term workaround. Eventually, Quorum Driver should be able
5350                // to return either an effects certificate, -or- a proof of inclusion in a checkpoint. In
5351                // the case above, the Quorum Driver would return a proof of inclusion in the final
5352                // checkpoint, and this code would no longer be necessary.
5353                if effects.executed_epoch() != epoch_store.epoch() {
5354                    debug!(
5355                        tx_digest=?transaction_digest,
5356                        effects_epoch=?effects.executed_epoch(),
5357                        epoch=?epoch_store.epoch(),
5358                        "Re-signing the effects with the current epoch"
5359                    );
5360                }
5361                Ok(Some(self.sign_effects(effects, epoch_store)?))
5362            }
5363            None => Ok(None),
5364        }
5365    }
5366
5367    /// A client aggregating effects signatures towards a quorum assumes finality once it
5368    /// collects 2f+1 of them, so within an epoch this validator must never assert two
5369    /// different effects for the same transaction on any RPC surface, signed or unsigned.
5370    /// Executed effects can change across a restart if an uncommitted transaction is
5371    /// re-executed with divergent results (e.g. by a new binary), so every effects-reporting
5372    /// path calls this before returning effects, and refuses to contradict a signature that
5373    /// may already be in a client's hands.
5374    pub fn check_effects_against_previously_signed(
5375        &self,
5376        epoch_store: &AuthorityPerEpochStore,
5377        tx_digest: &TransactionDigest,
5378        effects_digest: &TransactionEffectsDigest,
5379        surface: &'static str,
5380    ) -> SuiResult<()> {
5381        if let Some(previously_signed_digest) = epoch_store.get_signed_effects_digest(tx_digest)?
5382            && previously_signed_digest != *effects_digest
5383        {
5384            self.metrics
5385                .signed_effects_equivocation_prevented
5386                .with_label_values(&[surface])
5387                .inc();
5388            error!(
5389                ?tx_digest,
5390                ?previously_signed_digest,
5391                executed_digest = ?effects_digest,
5392                surface,
5393                "refusing to report effects that differ from previously signed effects"
5394            );
5395            return Err(SuiErrorKind::GenericAuthorityError {
5396                error: format!(
5397                    "Refusing to report effects for transaction {tx_digest}: effects digest \
5398                     {effects_digest} differs from previously signed effects digest \
5399                     {previously_signed_digest}"
5400                ),
5401            }
5402            .into());
5403        }
5404        Ok(())
5405    }
5406
5407    #[instrument(level = "trace", skip_all)]
5408    pub(crate) fn sign_effects(
5409        &self,
5410        effects: TransactionEffects,
5411        epoch_store: &Arc<AuthorityPerEpochStore>,
5412    ) -> SuiResult<VerifiedSignedTransactionEffects> {
5413        let tx_digest = *effects.transaction_digest();
5414
5415        self.check_effects_against_previously_signed(
5416            epoch_store,
5417            &tx_digest,
5418            &effects.digest(),
5419            "sign_effects",
5420        )?;
5421
5422        let signed_effects = match epoch_store.get_effects_signature(&tx_digest)? {
5423            Some(sig) => {
5424                debug_assert!(sig.epoch == epoch_store.epoch());
5425                SignedTransactionEffects::new_from_data_and_sig(effects, sig)
5426            }
5427            _ => {
5428                let sig = AuthoritySignInfo::new(
5429                    epoch_store.epoch(),
5430                    &effects,
5431                    Intent::sui_app(IntentScope::TransactionEffects),
5432                    self.name,
5433                    &*self.secret,
5434                );
5435
5436                let effects = SignedTransactionEffects::new_from_data_and_sig(effects, sig.clone());
5437
5438                epoch_store.insert_effects_digest_and_signature(
5439                    &tx_digest,
5440                    effects.digest(),
5441                    &sig,
5442                )?;
5443
5444                effects
5445            }
5446        };
5447
5448        Ok(VerifiedSignedTransactionEffects::new_unchecked(
5449            signed_effects,
5450        ))
5451    }
5452
5453    // Returns coin objects for indexing for fullnode if indexing is enabled.
5454    #[instrument(level = "trace", skip_all)]
5455    fn fullnode_only_get_tx_coins_for_indexing(
5456        name: AuthorityName,
5457        object_store: &Arc<dyn ObjectStore + Send + Sync>,
5458        effects: &TransactionEffects,
5459        inner_temporary_store: &InnerTemporaryStore,
5460        epoch_store: &Arc<AuthorityPerEpochStore>,
5461    ) -> Option<TxCoins> {
5462        if epoch_store.committee().authority_exists(&name) {
5463            return None;
5464        }
5465        let written_coin_objects = inner_temporary_store
5466            .written
5467            .iter()
5468            .filter_map(|(k, v)| {
5469                if v.is_coin() {
5470                    Some((*k, v.clone()))
5471                } else {
5472                    None
5473                }
5474            })
5475            .collect();
5476        let mut input_coin_objects = inner_temporary_store
5477            .input_objects
5478            .iter()
5479            .filter_map(|(k, v)| {
5480                if v.is_coin() {
5481                    Some((*k, v.clone()))
5482                } else {
5483                    None
5484                }
5485            })
5486            .collect::<ObjectMap>();
5487
5488        // Check for receiving objects that were actually used and modified during execution. Their
5489        // updated version will already showup in "written_coins" but their input isn't included in
5490        // the set of input objects in a inner_temporary_store.
5491        for (object_id, version) in effects.modified_at_versions() {
5492            if inner_temporary_store
5493                .loaded_runtime_objects
5494                .contains_key(&object_id)
5495                && let Some(object) = object_store.get_object_by_key(&object_id, version)
5496                && object.is_coin()
5497            {
5498                input_coin_objects.insert(object_id, object);
5499            }
5500        }
5501
5502        Some((input_coin_objects, written_coin_objects))
5503    }
5504
5505    pub fn get_objects(&self, objects: &[ObjectID]) -> Vec<Option<Object>> {
5506        self.get_object_cache_reader().get_objects(objects)
5507    }
5508
5509    pub fn get_object_or_tombstone(&self, object_id: ObjectID) -> Option<ObjectRef> {
5510        self.get_object_cache_reader()
5511            .get_latest_object_ref_or_tombstone(object_id)
5512    }
5513
5514    /// Ordinarily, protocol upgrades occur when 2f + 1 + (f *
5515    /// ProtocolConfig::buffer_stake_for_protocol_upgrade_bps) vote for the upgrade.
5516    ///
5517    /// This method can be used to dynamic adjust the amount of buffer. If set to 0, the upgrade
5518    /// will go through with only 2f+1 votes.
5519    ///
5520    /// IMPORTANT: If this is used, it must be used on >=2f+1 validators (all should have the same
5521    /// value), or you risk halting the chain.
5522    pub fn set_override_protocol_upgrade_buffer_stake(
5523        &self,
5524        expected_epoch: EpochId,
5525        buffer_stake_bps: u64,
5526    ) -> SuiResult {
5527        let epoch_store = self.load_epoch_store_one_call_per_task();
5528        let actual_epoch = epoch_store.epoch();
5529        if actual_epoch != expected_epoch {
5530            return Err(SuiErrorKind::WrongEpoch {
5531                expected_epoch,
5532                actual_epoch,
5533            }
5534            .into());
5535        }
5536
5537        epoch_store.set_override_protocol_upgrade_buffer_stake(buffer_stake_bps)
5538    }
5539
5540    pub fn clear_override_protocol_upgrade_buffer_stake(
5541        &self,
5542        expected_epoch: EpochId,
5543    ) -> SuiResult {
5544        let epoch_store = self.load_epoch_store_one_call_per_task();
5545        let actual_epoch = epoch_store.epoch();
5546        if actual_epoch != expected_epoch {
5547            return Err(SuiErrorKind::WrongEpoch {
5548                expected_epoch,
5549                actual_epoch,
5550            }
5551            .into());
5552        }
5553
5554        epoch_store.clear_override_protocol_upgrade_buffer_stake()
5555    }
5556
5557    /// Get the set of system packages that are compiled in to this build, if those packages are
5558    /// compatible with the current versions of those packages on-chain.
5559    pub async fn get_available_system_packages(
5560        &self,
5561        binary_config: &BinaryConfig,
5562    ) -> Vec<ObjectRef> {
5563        let mut results = vec![];
5564
5565        let system_packages = BuiltInFramework::iter_system_packages();
5566
5567        // Add extra framework packages during simtest
5568        #[cfg(msim)]
5569        let extra_packages = framework_injection::get_extra_packages(self.name);
5570        #[cfg(msim)]
5571        let system_packages = system_packages.map(|p| p).chain(extra_packages.iter());
5572
5573        for system_package in system_packages {
5574            let modules = system_package.modules().to_vec();
5575            // In simtests, we could override the current built-in framework packages.
5576            #[cfg(msim)]
5577            let modules =
5578                match framework_injection::get_override_modules(&system_package.id, self.name) {
5579                    Some(overrides) if overrides.is_empty() => continue,
5580                    Some(overrides) => overrides,
5581                    None => modules,
5582                };
5583
5584            let Some(obj_ref) = sui_framework::compare_system_package(
5585                &self.get_object_store(),
5586                &system_package.id,
5587                &modules,
5588                system_package.dependencies.to_vec(),
5589                binary_config,
5590            )
5591            .await
5592            else {
5593                return vec![];
5594            };
5595            results.push(obj_ref);
5596        }
5597
5598        results
5599    }
5600
5601    /// Return the new versions, module bytes, and dependencies for the packages that have been
5602    /// committed to for a framework upgrade, in `system_packages`.  Loads the module contents from
5603    /// the binary, and performs the following checks:
5604    ///
5605    /// - Whether its contents matches what is on-chain already, in which case no upgrade is
5606    ///   required, and its contents are omitted from the output.
5607    /// - Whether the contents in the binary can form a package whose digest matches the input,
5608    ///   meaning the framework will be upgraded, and this authority can satisfy that upgrade, in
5609    ///   which case the contents are included in the output.
5610    ///
5611    /// If a needed version of the framework can't be loaded, the binary does not contain the
5612    /// bytes for that framework ID, or the resulting package fails the digest check, `None` is
5613    /// returned indicating that this authority cannot run the upgrade that the network voted on.
5614    ///
5615    /// All object lookups are pinned to the versions in `system_packages` instead of using the
5616    /// latest versions, so that the result is deterministic even if the change epoch transaction
5617    /// that performs the upgrade has already been executed locally (e.g. via state sync). In that
5618    /// case the reconstructed change epoch transaction is byte-identical to the executed one, and
5619    /// the caller detects it as already executed.
5620    async fn get_system_package_bytes(
5621        &self,
5622        system_packages: Vec<ObjectRef>,
5623        binary_config: &BinaryConfig,
5624    ) -> Option<Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>> {
5625        let object_store = self.get_object_cache_reader();
5626
5627        let mut res = Vec::with_capacity(system_packages.len());
5628        for system_package_ref in system_packages {
5629            if let Some(object) =
5630                object_store.get_object_by_key(&system_package_ref.0, system_package_ref.1)
5631                && object.compute_object_reference() == system_package_ref
5632            {
5633                // Skip this one because it doesn't need to be upgraded.
5634                info!("Framework {} does not need updating", system_package_ref.0);
5635                continue;
5636            }
5637
5638            // The digest in `system_package_ref` commits to a package built on top of the
5639            // predecessor version's `previous_transaction` (see `compare_system_package`), so it
5640            // must be re-derived from that version. A ref at `OBJECT_START_VERSION` is a freshly
5641            // created package with no predecessor.
5642            let prev_transaction = if system_package_ref.1 == OBJECT_START_VERSION {
5643                TransactionDigest::genesis_marker()
5644            } else {
5645                let prev_version = system_package_ref
5646                    .1
5647                    .one_before()
5648                    .expect("version is greater than OBJECT_START_VERSION");
5649                let Some(prev_object) =
5650                    object_store.get_object_by_key(&system_package_ref.0, prev_version)
5651                else {
5652                    error!(
5653                        "Framework {} not available locally at version {:?}, cannot derive \
5654                        upgrade to {system_package_ref:?}",
5655                        system_package_ref.0, prev_version
5656                    );
5657                    return None;
5658                };
5659                prev_object.previous_transaction
5660            };
5661
5662            #[cfg(msim)]
5663            let SystemPackage {
5664                id: _,
5665                bytes,
5666                dependencies,
5667            } = framework_injection::get_override_system_package(&system_package_ref.0, self.name)
5668                .unwrap_or_else(|| {
5669                    BuiltInFramework::get_package_by_id(&system_package_ref.0).clone()
5670                });
5671
5672            #[cfg(not(msim))]
5673            let SystemPackage {
5674                id: _,
5675                bytes,
5676                dependencies,
5677            } = BuiltInFramework::get_package_by_id(&system_package_ref.0).clone();
5678
5679            let modules: Vec<_> = bytes
5680                .iter()
5681                .map(|m| CompiledModule::deserialize_with_config(m, binary_config).unwrap())
5682                .collect();
5683
5684            let new_object = Object::new_system_package(
5685                &modules,
5686                system_package_ref.1,
5687                dependencies.clone(),
5688                prev_transaction,
5689            );
5690
5691            let new_ref = new_object.compute_object_reference();
5692            if new_ref != system_package_ref {
5693                if cfg!(msim) {
5694                    // debug_fatal required here for test_framework_upgrade_conflicting_versions to pass
5695                    debug_fatal!(
5696                        "Framework mismatch -- binary: {new_ref:?}\n  upgrade: {system_package_ref:?}"
5697                    );
5698                } else {
5699                    error!(
5700                        "Framework mismatch -- binary: {new_ref:?}\n  upgrade: {system_package_ref:?}"
5701                    );
5702                }
5703                return None;
5704            }
5705
5706            res.push((system_package_ref.1, bytes, dependencies));
5707        }
5708
5709        Some(res)
5710    }
5711
5712    fn is_protocol_version_supported_v2(
5713        current_protocol_version: ProtocolVersion,
5714        proposed_protocol_version: ProtocolVersion,
5715        protocol_config: &ProtocolConfig,
5716        committee: &Committee,
5717        capabilities: Vec<AuthorityCapabilitiesV2>,
5718        mut buffer_stake_bps: u64,
5719    ) -> Option<(ProtocolVersion, Vec<ObjectRef>)> {
5720        if proposed_protocol_version > current_protocol_version + 1
5721            && !protocol_config.advance_to_highest_supported_protocol_version()
5722        {
5723            return None;
5724        }
5725
5726        if buffer_stake_bps > 10000 {
5727            warn!("clamping buffer_stake_bps to 10000");
5728            buffer_stake_bps = 10000;
5729        }
5730
5731        // For each validator, gather the protocol version and system packages that it would like
5732        // to upgrade to in the next epoch.
5733        let mut desired_upgrades: Vec<_> = capabilities
5734            .into_iter()
5735            .filter_map(|mut cap| {
5736                // A validator that lists no packages is voting against any change at all.
5737                if cap.available_system_packages.is_empty() {
5738                    return None;
5739                }
5740
5741                cap.available_system_packages.sort();
5742
5743                info!(
5744                    "validator {:?} supports {:?} with system packages: {:?}",
5745                    cap.authority.concise(),
5746                    cap.supported_protocol_versions,
5747                    cap.available_system_packages,
5748                );
5749
5750                // A validator that only supports the current protocol version is also voting
5751                // against any change, because framework upgrades always require a protocol version
5752                // bump.
5753                cap.supported_protocol_versions
5754                    .get_version_digest(proposed_protocol_version)
5755                    .map(|digest| (digest, cap.available_system_packages, cap.authority))
5756            })
5757            .collect();
5758
5759        // There can only be one set of votes that have a majority, find one if it exists.
5760        desired_upgrades.sort();
5761        desired_upgrades
5762            .into_iter()
5763            .chunk_by(|(digest, packages, _authority)| (*digest, packages.clone()))
5764            .into_iter()
5765            .find_map(|((digest, packages), group)| {
5766                // should have been filtered out earlier.
5767                assert!(!packages.is_empty());
5768
5769                let mut stake_aggregator: StakeAggregator<(), true> =
5770                    StakeAggregator::new(Arc::new(committee.clone()));
5771
5772                for (_, _, authority) in group {
5773                    stake_aggregator.insert_generic(authority, ());
5774                }
5775
5776                let total_votes = stake_aggregator.total_votes();
5777                let quorum_threshold = committee.quorum_threshold();
5778                let f = committee.total_votes() - committee.quorum_threshold();
5779
5780                // multiple by buffer_stake_bps / 10000, rounded up.
5781                let buffer_stake = (f * buffer_stake_bps).div_ceil(10000);
5782                let effective_threshold = quorum_threshold + buffer_stake;
5783
5784                info!(
5785                    protocol_config_digest = ?digest,
5786                    ?total_votes,
5787                    ?quorum_threshold,
5788                    ?buffer_stake_bps,
5789                    ?effective_threshold,
5790                    ?proposed_protocol_version,
5791                    ?packages,
5792                    "support for upgrade"
5793                );
5794
5795                let has_support = total_votes >= effective_threshold;
5796                has_support.then_some((proposed_protocol_version, packages))
5797            })
5798    }
5799
5800    fn choose_protocol_version_and_system_packages_v2(
5801        current_protocol_version: ProtocolVersion,
5802        protocol_config: &ProtocolConfig,
5803        committee: &Committee,
5804        capabilities: Vec<AuthorityCapabilitiesV2>,
5805        buffer_stake_bps: u64,
5806    ) -> (ProtocolVersion, Vec<ObjectRef>) {
5807        assert!(protocol_config.authority_capabilities_v2());
5808        let mut next_protocol_version = current_protocol_version;
5809        let mut system_packages = vec![];
5810
5811        while let Some((version, packages)) = Self::is_protocol_version_supported_v2(
5812            current_protocol_version,
5813            next_protocol_version + 1,
5814            protocol_config,
5815            committee,
5816            capabilities.clone(),
5817            buffer_stake_bps,
5818        ) {
5819            next_protocol_version = version;
5820            system_packages = packages;
5821        }
5822
5823        (next_protocol_version, system_packages)
5824    }
5825
5826    #[instrument(level = "debug", skip_all)]
5827    fn create_authenticator_state_tx(
5828        &self,
5829        epoch_store: &Arc<AuthorityPerEpochStore>,
5830    ) -> Option<EndOfEpochTransactionKind> {
5831        if !epoch_store.protocol_config().enable_jwk_consensus_updates() {
5832            info!("authenticator state transactions not enabled");
5833            return None;
5834        }
5835
5836        let authenticator_state_exists = epoch_store.authenticator_state_exists();
5837        let tx = if authenticator_state_exists {
5838            let next_epoch = epoch_store.epoch().checked_add(1).expect("epoch overflow");
5839            let min_epoch =
5840                next_epoch.saturating_sub(epoch_store.protocol_config().max_age_of_jwk_in_epochs());
5841            let authenticator_obj_initial_shared_version = epoch_store
5842                .epoch_start_config()
5843                .authenticator_obj_initial_shared_version()
5844                .expect("initial version must exist");
5845
5846            let tx = EndOfEpochTransactionKind::new_authenticator_state_expire(
5847                min_epoch,
5848                authenticator_obj_initial_shared_version,
5849            );
5850
5851            info!(?min_epoch, "Creating AuthenticatorStateExpire tx",);
5852
5853            tx
5854        } else {
5855            let tx = EndOfEpochTransactionKind::new_authenticator_state_create();
5856            info!("Creating AuthenticatorStateCreate tx");
5857            tx
5858        };
5859        Some(tx)
5860    }
5861
5862    #[instrument(level = "debug", skip_all)]
5863    fn create_randomness_state_tx(
5864        &self,
5865        epoch_store: &Arc<AuthorityPerEpochStore>,
5866    ) -> Option<EndOfEpochTransactionKind> {
5867        if !epoch_store.protocol_config().random_beacon() {
5868            info!("randomness state transactions not enabled");
5869            return None;
5870        }
5871
5872        if epoch_store.randomness_state_exists() {
5873            return None;
5874        }
5875
5876        let tx = EndOfEpochTransactionKind::new_randomness_state_create();
5877        info!("Creating RandomnessStateCreate tx");
5878        Some(tx)
5879    }
5880
5881    #[instrument(level = "debug", skip_all)]
5882    fn create_accumulator_root_tx(
5883        &self,
5884        epoch_store: &Arc<AuthorityPerEpochStore>,
5885    ) -> Option<EndOfEpochTransactionKind> {
5886        if !epoch_store
5887            .protocol_config()
5888            .create_root_accumulator_object()
5889        {
5890            info!("accumulator root creation not enabled");
5891            return None;
5892        }
5893
5894        if epoch_store.accumulator_root_exists() {
5895            return None;
5896        }
5897
5898        let tx = EndOfEpochTransactionKind::new_accumulator_root_create();
5899        info!("Creating AccumulatorRootCreate tx");
5900        Some(tx)
5901    }
5902
5903    #[instrument(level = "debug", skip_all)]
5904    fn create_write_accumulator_storage_cost_tx(
5905        &self,
5906        epoch_store: &Arc<AuthorityPerEpochStore>,
5907    ) -> Option<EndOfEpochTransactionKind> {
5908        if !epoch_store.accumulator_root_exists() {
5909            info!("accumulator root does not exist yet");
5910            return None;
5911        }
5912        if !epoch_store.protocol_config().enable_accumulators() {
5913            info!("accumulators not enabled");
5914            return None;
5915        }
5916
5917        let object_store = self.get_object_store();
5918        let object_count =
5919            match accumulator_metadata::get_accumulator_object_count(object_store.as_ref()) {
5920                Ok(Some(count)) => count,
5921                Ok(None) => return None,
5922                Err(e) => {
5923                    fatal!("failed to read accumulator object count: {e}");
5924                }
5925            };
5926
5927        let storage_cost = object_count.saturating_mul(
5928            epoch_store
5929                .protocol_config()
5930                .accumulator_object_storage_cost(),
5931        );
5932
5933        let tx = EndOfEpochTransactionKind::new_write_accumulator_storage_cost(storage_cost);
5934        info!(
5935            object_count,
5936            storage_cost, "Creating WriteAccumulatorStorageCost tx"
5937        );
5938        Some(tx)
5939    }
5940
5941    #[instrument(level = "debug", skip_all)]
5942    fn create_coin_registry_tx(
5943        &self,
5944        epoch_store: &Arc<AuthorityPerEpochStore>,
5945    ) -> Option<EndOfEpochTransactionKind> {
5946        if !epoch_store.protocol_config().enable_coin_registry() {
5947            info!("coin registry not enabled");
5948            return None;
5949        }
5950
5951        if epoch_store.coin_registry_exists() {
5952            return None;
5953        }
5954
5955        let tx = EndOfEpochTransactionKind::new_coin_registry_create();
5956        info!("Creating CoinRegistryCreate tx");
5957        Some(tx)
5958    }
5959
5960    #[instrument(level = "debug", skip_all)]
5961    fn create_display_registry_tx(
5962        &self,
5963        epoch_store: &Arc<AuthorityPerEpochStore>,
5964    ) -> Option<EndOfEpochTransactionKind> {
5965        if !epoch_store.protocol_config().enable_display_registry() {
5966            info!("display registry not enabled");
5967            return None;
5968        }
5969
5970        if epoch_store.display_registry_exists() {
5971            return None;
5972        }
5973
5974        let tx = EndOfEpochTransactionKind::new_display_registry_create();
5975        info!("Creating DisplayRegistryCreate tx");
5976        Some(tx)
5977    }
5978
5979    #[instrument(level = "debug", skip_all)]
5980    fn create_bridge_tx(
5981        &self,
5982        epoch_store: &Arc<AuthorityPerEpochStore>,
5983    ) -> Option<EndOfEpochTransactionKind> {
5984        if !epoch_store.protocol_config().bridge() {
5985            info!("bridge not enabled");
5986            return None;
5987        }
5988        if epoch_store.bridge_exists() {
5989            return None;
5990        }
5991        let tx = EndOfEpochTransactionKind::new_bridge_create(epoch_store.get_chain_identifier());
5992        info!("Creating Bridge Create tx");
5993        Some(tx)
5994    }
5995
5996    #[instrument(level = "debug", skip_all)]
5997    fn init_bridge_committee_tx(
5998        &self,
5999        epoch_store: &Arc<AuthorityPerEpochStore>,
6000    ) -> Option<EndOfEpochTransactionKind> {
6001        if !epoch_store.protocol_config().bridge() {
6002            info!("bridge not enabled");
6003            return None;
6004        }
6005        if !epoch_store
6006            .protocol_config()
6007            .should_try_to_finalize_bridge_committee()
6008        {
6009            info!("should not try to finalize bridge committee yet");
6010            return None;
6011        }
6012        // Only create this transaction if bridge exists
6013        if !epoch_store.bridge_exists() {
6014            return None;
6015        }
6016
6017        if epoch_store.bridge_committee_initiated() {
6018            return None;
6019        }
6020
6021        let bridge_initial_shared_version = epoch_store
6022            .epoch_start_config()
6023            .bridge_obj_initial_shared_version()
6024            .expect("initial version must exist");
6025        let tx = EndOfEpochTransactionKind::init_bridge_committee(bridge_initial_shared_version);
6026        info!("Init Bridge committee tx");
6027        Some(tx)
6028    }
6029
6030    #[instrument(level = "debug", skip_all)]
6031    fn create_deny_list_state_tx(
6032        &self,
6033        epoch_store: &Arc<AuthorityPerEpochStore>,
6034    ) -> Option<EndOfEpochTransactionKind> {
6035        if !epoch_store.protocol_config().enable_coin_deny_list() {
6036            return None;
6037        }
6038
6039        if epoch_store.coin_deny_list_state_exists() {
6040            return None;
6041        }
6042
6043        let tx = EndOfEpochTransactionKind::new_deny_list_state_create();
6044        info!("Creating DenyListStateCreate tx");
6045        Some(tx)
6046    }
6047
6048    #[instrument(level = "debug", skip_all)]
6049    fn create_address_alias_state_tx(
6050        &self,
6051        epoch_store: &Arc<AuthorityPerEpochStore>,
6052    ) -> Option<EndOfEpochTransactionKind> {
6053        if !epoch_store.protocol_config().address_aliases() {
6054            info!("address aliases not enabled");
6055            return None;
6056        }
6057
6058        if epoch_store.address_alias_state_exists() {
6059            return None;
6060        }
6061
6062        let tx = EndOfEpochTransactionKind::new_address_alias_state_create();
6063        info!("Creating AddressAliasStateCreate tx");
6064        Some(tx)
6065    }
6066
6067    #[instrument(level = "debug", skip_all)]
6068    fn create_forwarding_address_registry_tx(
6069        &self,
6070        epoch_store: &Arc<AuthorityPerEpochStore>,
6071    ) -> Option<EndOfEpochTransactionKind> {
6072        if !epoch_store
6073            .protocol_config()
6074            .create_forwarding_address_registry()
6075        {
6076            info!("forwarding address registry creation not enabled");
6077            return None;
6078        }
6079
6080        if epoch_store.forwarding_address_registry_exists() {
6081            return None;
6082        }
6083
6084        let tx = EndOfEpochTransactionKind::new_forwarding_address_registry_create();
6085        info!("Creating ForwardingAddressRegistryCreate tx");
6086        Some(tx)
6087    }
6088
6089    #[instrument(level = "debug", skip_all)]
6090    fn create_execution_time_observations_tx(
6091        &self,
6092        epoch_store: &Arc<AuthorityPerEpochStore>,
6093        end_of_epoch_observation_keys: Vec<ExecutionTimeObservationKey>,
6094        last_checkpoint_before_end_of_epoch: CheckpointSequenceNumber,
6095    ) -> Option<EndOfEpochTransactionKind> {
6096        let PerObjectCongestionControlMode::ExecutionTimeEstimate(params) = epoch_store
6097            .protocol_config()
6098            .per_object_congestion_control_mode()
6099        else {
6100            return None;
6101        };
6102
6103        // Load tx in the last N checkpoints before end-of-epoch, and save only the
6104        // execution time observations for commands in these checkpoints.
6105        let start_checkpoint = std::cmp::max(
6106            last_checkpoint_before_end_of_epoch
6107                .saturating_sub(params.stored_observations_num_included_checkpoints - 1),
6108            // If we have <N checkpoints in the epoch, use all of them.
6109            epoch_store
6110                .epoch()
6111                .checked_sub(1)
6112                .map(|prev_epoch| {
6113                    self.checkpoint_store
6114                        .get_epoch_last_checkpoint_seq_number(prev_epoch)
6115                        .expect("typed store must not fail")
6116                        .expect(
6117                            "sequence number of last checkpoint of preceding epoch must be saved",
6118                        )
6119                        + 1
6120                })
6121                .unwrap_or(1),
6122        );
6123        info!(
6124            "reading checkpoint range {:?}..={:?}",
6125            start_checkpoint, last_checkpoint_before_end_of_epoch
6126        );
6127        let sequence_numbers =
6128            (start_checkpoint..=last_checkpoint_before_end_of_epoch).collect::<Vec<_>>();
6129        let contents_digests: Vec<_> = self
6130            .checkpoint_store
6131            .multi_get_locally_computed_checkpoints(&sequence_numbers)
6132            .expect("typed store must not fail")
6133            .into_iter()
6134            .zip_debug_eq(sequence_numbers)
6135            .map(|(maybe_checkpoint, sequence_number)| {
6136                if let Some(checkpoint) = maybe_checkpoint {
6137                    checkpoint.content_digest
6138                } else {
6139                    // If locally computed checkpoint summary was already pruned, load the
6140                    // certified checkpoint.
6141                    self.checkpoint_store
6142                        .get_checkpoint_by_sequence_number(sequence_number)
6143                        .expect("typed store must not fail")
6144                        .unwrap_or_else(|| {
6145                            fatal!("preceding checkpoints must exist by end of epoch")
6146                        })
6147                        .data()
6148                        .content_digest
6149                }
6150            })
6151            .collect();
6152        let tx_digests: Vec<_> = self
6153            .checkpoint_store
6154            .multi_get_checkpoint_content(&contents_digests)
6155            .expect("typed store must not fail")
6156            .into_iter()
6157            .flat_map(|maybe_contents| {
6158                maybe_contents
6159                    .expect("preceding checkpoint contents must exist by end of epoch")
6160                    .into_inner()
6161                    .into_iter()
6162                    .map(|digests| digests.transaction)
6163            })
6164            .collect();
6165        let included_execution_time_observations: HashSet<_> = self
6166            .get_transaction_cache_reader()
6167            .multi_get_transaction_blocks(&tx_digests)
6168            .into_iter()
6169            .flat_map(|maybe_tx| {
6170                if let TransactionKind::ProgrammableTransaction(ptb) = maybe_tx
6171                    .expect("preceding transaction must exist by end of epoch")
6172                    .transaction_data()
6173                    .kind()
6174                {
6175                    #[allow(clippy::unnecessary_to_owned)]
6176                    itertools::Either::Left(
6177                        ptb.commands
6178                            .to_owned()
6179                            .into_iter()
6180                            .map(|cmd| ExecutionTimeObservationKey::from_command(&cmd)),
6181                    )
6182                } else {
6183                    itertools::Either::Right(std::iter::empty())
6184                }
6185            })
6186            .chain(end_of_epoch_observation_keys.into_iter())
6187            .collect();
6188
6189        let tx = EndOfEpochTransactionKind::new_store_execution_time_observations(
6190            epoch_store
6191                .get_end_of_epoch_execution_time_observations()
6192                .filter_and_sort_v1(
6193                    |(key, _)| included_execution_time_observations.contains(key),
6194                    params.stored_observations_limit.try_into().unwrap(),
6195                ),
6196        );
6197        info!("Creating StoreExecutionTimeObservations tx");
6198        Some(tx)
6199    }
6200
6201    /// Creates and execute the advance epoch transaction to effects without committing it to the database.
6202    /// The effects of the change epoch tx are only written to the database after a certified checkpoint has been
6203    /// formed and executed by CheckpointExecutor.
6204    ///
6205    /// When a framework upgraded has been decided on, but the validator does not have the new
6206    /// versions of the packages locally, the validator cannot form the ChangeEpochTx. In this case
6207    /// it returns Err, indicating that the checkpoint builder should give up trying to make the
6208    /// final checkpoint. As long as the network is able to create a certified checkpoint (which
6209    /// should be ensured by the capabilities vote), it will arrive via state sync and be executed
6210    /// by CheckpointExecutor.
6211    #[instrument(level = "error", skip_all)]
6212    pub async fn create_and_execute_advance_epoch_tx(
6213        &self,
6214        epoch_store: &Arc<AuthorityPerEpochStore>,
6215        gas_cost_summary: &GasCostSummary,
6216        checkpoint: CheckpointSequenceNumber,
6217        epoch_start_timestamp_ms: CheckpointTimestamp,
6218        end_of_epoch_observation_keys: Vec<ExecutionTimeObservationKey>,
6219        // This may be less than `checkpoint - 1` if the end-of-epoch PendingCheckpoint produced
6220        // >1 checkpoint.
6221        last_checkpoint: CheckpointSequenceNumber,
6222    ) -> CheckpointBuilderResult<(SuiSystemState, TransactionEffects)> {
6223        let mut txns = Vec::new();
6224
6225        if let Some(tx) = self.create_authenticator_state_tx(epoch_store) {
6226            txns.push(tx);
6227        }
6228        if let Some(tx) = self.create_randomness_state_tx(epoch_store) {
6229            txns.push(tx);
6230        }
6231        if let Some(tx) = self.create_bridge_tx(epoch_store) {
6232            txns.push(tx);
6233        }
6234        if let Some(tx) = self.init_bridge_committee_tx(epoch_store) {
6235            txns.push(tx);
6236        }
6237        if let Some(tx) = self.create_deny_list_state_tx(epoch_store) {
6238            txns.push(tx);
6239        }
6240        if let Some(tx) = self.create_execution_time_observations_tx(
6241            epoch_store,
6242            end_of_epoch_observation_keys,
6243            last_checkpoint,
6244        ) {
6245            txns.push(tx);
6246        }
6247        if let Some(tx) = self.create_accumulator_root_tx(epoch_store) {
6248            txns.push(tx);
6249        }
6250
6251        if let Some(tx) = self.create_coin_registry_tx(epoch_store) {
6252            txns.push(tx);
6253        }
6254        if let Some(tx) = self.create_display_registry_tx(epoch_store) {
6255            txns.push(tx);
6256        }
6257        if let Some(tx) = self.create_address_alias_state_tx(epoch_store) {
6258            txns.push(tx);
6259        }
6260        if let Some(tx) = self.create_forwarding_address_registry_tx(epoch_store) {
6261            txns.push(tx);
6262        }
6263        if let Some(tx) = self.create_write_accumulator_storage_cost_tx(epoch_store) {
6264            txns.push(tx);
6265        }
6266
6267        let next_epoch = epoch_store.epoch() + 1;
6268
6269        let buffer_stake_bps = epoch_store.get_effective_buffer_stake_bps();
6270
6271        let (next_epoch_protocol_version, next_epoch_system_packages) =
6272            Self::choose_protocol_version_and_system_packages_v2(
6273                epoch_store.protocol_version(),
6274                epoch_store.protocol_config(),
6275                epoch_store.committee(),
6276                epoch_store
6277                    .get_capabilities_v2()
6278                    .expect("read capabilities from db cannot fail"),
6279                buffer_stake_bps,
6280            );
6281
6282        // since system packages are created during the current epoch, they should abide by the
6283        // rules of the current epoch, including the current epoch's max Move binary format version
6284        let config = epoch_store.protocol_config();
6285        let binary_config = config.binary_config(None);
6286        let Some(next_epoch_system_package_bytes) = self
6287            .get_system_package_bytes(next_epoch_system_packages.clone(), &binary_config)
6288            .await
6289        else {
6290            if next_epoch_protocol_version <= ProtocolVersion::MAX {
6291                // This case should only be hit if the validator supports the new protocol version,
6292                // but carries a different framework. The validator should still be able to
6293                // reconfigure, as the correct framework will be installed by the change epoch txn.
6294                debug_fatal!(
6295                    "upgraded system packages {:?} are not locally available, cannot create \
6296                    ChangeEpochTx. validator binary must be upgraded to the correct version!",
6297                    next_epoch_system_packages
6298                );
6299            } else {
6300                error!(
6301                    "validator does not support next_epoch_protocol_version {:?} - will shut down after reconfig unless upgraded",
6302                    next_epoch_protocol_version
6303                );
6304            }
6305            // the checkpoint builder will keep retrying forever when it hits this error.
6306            // Eventually, one of two things will happen:
6307            // - The operator will upgrade this binary to one that has the new packages locally,
6308            //   and this function will succeed.
6309            // - The final checkpoint will be certified by other validators, we will receive it via
6310            //   state sync, and execute it. This will upgrade the framework packages, reconfigure,
6311            //   and most likely shut down in the new epoch (this validator likely doesn't support
6312            //   the new protocol version, or else it should have had the packages.)
6313            return Err(CheckpointBuilderError::SystemPackagesMissing);
6314        };
6315
6316        let tx = if epoch_store
6317            .protocol_config()
6318            .end_of_epoch_transaction_supported()
6319        {
6320            txns.push(EndOfEpochTransactionKind::new_change_epoch(
6321                next_epoch,
6322                next_epoch_protocol_version,
6323                gas_cost_summary.storage_cost,
6324                gas_cost_summary.computation_cost,
6325                gas_cost_summary.storage_rebate,
6326                gas_cost_summary.non_refundable_storage_fee,
6327                epoch_start_timestamp_ms,
6328                next_epoch_system_package_bytes,
6329            ));
6330
6331            VerifiedTransaction::new_end_of_epoch_transaction(txns)
6332        } else {
6333            VerifiedTransaction::new_change_epoch(
6334                next_epoch,
6335                next_epoch_protocol_version,
6336                gas_cost_summary.storage_cost,
6337                gas_cost_summary.computation_cost,
6338                gas_cost_summary.storage_rebate,
6339                gas_cost_summary.non_refundable_storage_fee,
6340                epoch_start_timestamp_ms,
6341                next_epoch_system_package_bytes,
6342            )
6343        };
6344
6345        let executable_tx = VerifiedExecutableTransaction::new_from_checkpoint(
6346            tx.clone(),
6347            epoch_store.epoch(),
6348            checkpoint,
6349        );
6350
6351        let tx_digest = executable_tx.digest();
6352
6353        info!(
6354            ?next_epoch,
6355            ?next_epoch_protocol_version,
6356            ?next_epoch_system_packages,
6357            computation_cost=?gas_cost_summary.computation_cost,
6358            storage_cost=?gas_cost_summary.storage_cost,
6359            storage_rebate=?gas_cost_summary.storage_rebate,
6360            non_refundable_storage_fee=?gas_cost_summary.non_refundable_storage_fee,
6361            ?tx_digest,
6362            "Creating advance epoch transaction"
6363        );
6364
6365        fail_point_async!("change_epoch_tx_delay");
6366        let tx_lock = epoch_store.acquire_tx_lock(tx_digest);
6367
6368        // The tx could have been executed by state sync already - if so simply return an error.
6369        // The checkpoint builder will shortly be terminated by reconfiguration anyway.
6370        if self
6371            .get_transaction_cache_reader()
6372            .is_tx_already_executed(tx_digest)
6373        {
6374            warn!("change epoch tx has already been executed via state sync");
6375            return Err(CheckpointBuilderError::ChangeEpochTxAlreadyExecuted);
6376        }
6377
6378        let Some(execution_guard) = self.execution_lock_for_executable_transaction(&executable_tx)
6379        else {
6380            return Err(CheckpointBuilderError::ChangeEpochTxAlreadyExecuted);
6381        };
6382
6383        // We must manually assign the shared object versions to the transaction before executing it.
6384        // This is because we do not sequence end-of-epoch transactions through consensus.
6385        let assigned_versions = epoch_store.assign_shared_object_versions_idempotent(
6386            self.get_object_cache_reader().as_ref(),
6387            std::iter::once(&Schedulable::Transaction(&executable_tx)),
6388        )?;
6389
6390        assert_eq!(assigned_versions.0.len(), 1);
6391        let assigned_versions = assigned_versions.0.into_iter().next().unwrap().1;
6392
6393        let input_objects = self.read_objects_for_execution(
6394            &tx_lock,
6395            &executable_tx,
6396            &assigned_versions,
6397            epoch_store,
6398        )?;
6399
6400        let (transaction_outputs, _timings, _execution_error_opt) = self
6401            .execute_certificate(
6402                &execution_guard,
6403                &executable_tx,
6404                input_objects,
6405                None,
6406                ExecutionEnv::default(),
6407                epoch_store,
6408            )
6409            .unwrap();
6410        let system_obj = get_sui_system_state(&transaction_outputs.written)
6411            .expect("change epoch tx must write to system object");
6412
6413        let effects = transaction_outputs.effects;
6414        // We must write tx and effects to the state sync tables so that state sync is able to
6415        // deliver to the transaction to CheckpointExecutor after it is included in a certified
6416        // checkpoint.
6417        self.get_state_sync_store()
6418            .insert_transaction_and_effects(&tx, &effects);
6419
6420        info!(
6421            "Effects summary of the change epoch transaction: {:?}",
6422            effects.summary_for_debug()
6423        );
6424        epoch_store.record_checkpoint_builder_is_safe_mode_metric(system_obj.safe_mode());
6425        // The change epoch transaction cannot fail to execute.
6426        assert!(effects.status().is_ok());
6427        Ok((system_obj, effects))
6428    }
6429
6430    #[instrument(level = "error", skip_all)]
6431    async fn reopen_epoch_db(
6432        &self,
6433        cur_epoch_store: &AuthorityPerEpochStore,
6434        new_committee: Committee,
6435        epoch_start_configuration: EpochStartConfiguration,
6436        expensive_safety_check_config: &ExpensiveSafetyCheckConfig,
6437        epoch_last_checkpoint: CheckpointSequenceNumber,
6438    ) -> SuiResult<Arc<AuthorityPerEpochStore>> {
6439        let new_epoch = new_committee.epoch;
6440        info!(new_epoch = ?new_epoch, "re-opening AuthorityEpochTables for new epoch");
6441        assert_eq!(
6442            epoch_start_configuration.epoch_start_state().epoch(),
6443            new_committee.epoch
6444        );
6445        fail_point!("before-open-new-epoch-store");
6446        let new_epoch_store = cur_epoch_store.new_at_next_epoch(
6447            self.name,
6448            new_committee,
6449            epoch_start_configuration,
6450            self.get_backing_package_store().clone(),
6451            self.get_object_store().clone(),
6452            expensive_safety_check_config,
6453            epoch_last_checkpoint,
6454            self.config.fullnode_sync_mode,
6455        )?;
6456        self.epoch_store.store(new_epoch_store.clone());
6457        Ok(new_epoch_store)
6458    }
6459
6460    #[cfg(test)]
6461    pub(crate) fn iter_live_object_set_for_testing(
6462        &self,
6463    ) -> impl Iterator<Item = authority_store_tables::LiveObject> + '_ {
6464        let include_wrapped_object = !self
6465            .epoch_store_for_testing()
6466            .protocol_config()
6467            .simplified_unwrap_then_delete();
6468        self.get_global_state_hash_store()
6469            .iter_cached_live_object_set_for_testing(include_wrapped_object)
6470    }
6471
6472    #[cfg(test)]
6473    pub(crate) fn shutdown_execution_for_test(&self) {
6474        self.tx_execution_shutdown
6475            .lock()
6476            .take()
6477            .unwrap()
6478            .send(())
6479            .unwrap();
6480    }
6481
6482    /// NOTE: this function is only to be used for fuzzing and testing. Never use in prod
6483    pub async fn insert_objects_unsafe_for_testing_only(&self, objects: &[Object]) -> SuiResult {
6484        self.get_reconfig_api().bulk_insert_genesis_objects(objects);
6485        self.get_object_cache_reader()
6486            .force_reload_system_packages(&BuiltInFramework::all_package_ids());
6487        self.get_reconfig_api()
6488            .clear_state_end_of_epoch(&self.execution_lock_for_reconfiguration().await);
6489        Ok(())
6490    }
6491}
6492
6493#[async_trait]
6494impl TransactionKeyValueStoreTrait for AuthorityState {
6495    #[instrument(skip(self))]
6496    async fn multi_get(
6497        &self,
6498        transactions: &[TransactionDigest],
6499        effects: &[TransactionDigest],
6500    ) -> SuiResult<(Vec<Option<Transaction>>, Vec<Option<TransactionEffects>>)> {
6501        let txns = if !transactions.is_empty() {
6502            self.get_transaction_cache_reader()
6503                .multi_get_transaction_blocks(transactions)
6504                .into_iter()
6505                .map(|t| t.map(|t| (*t).clone().into_inner()))
6506                .collect()
6507        } else {
6508            vec![]
6509        };
6510
6511        let fx = if !effects.is_empty() {
6512            self.get_transaction_cache_reader()
6513                .multi_get_executed_effects(effects)
6514        } else {
6515            vec![]
6516        };
6517
6518        Ok((txns, fx))
6519    }
6520
6521    #[instrument(skip(self))]
6522    async fn multi_get_checkpoints(
6523        &self,
6524        checkpoint_summaries: &[CheckpointSequenceNumber],
6525        checkpoint_contents: &[CheckpointSequenceNumber],
6526        checkpoint_summaries_by_digest: &[CheckpointDigest],
6527    ) -> SuiResult<(
6528        Vec<Option<CertifiedCheckpointSummary>>,
6529        Vec<Option<CheckpointContents>>,
6530        Vec<Option<CertifiedCheckpointSummary>>,
6531    )> {
6532        // TODO: use multi-get methods if it ever becomes important (unlikely)
6533        let mut summaries = Vec::with_capacity(checkpoint_summaries.len());
6534        let store = self.get_checkpoint_store();
6535        for seq in checkpoint_summaries {
6536            let checkpoint = store
6537                .get_checkpoint_by_sequence_number(*seq)?
6538                .map(|c| c.into_inner());
6539
6540            summaries.push(checkpoint);
6541        }
6542
6543        let mut contents = Vec::with_capacity(checkpoint_contents.len());
6544        for seq in checkpoint_contents {
6545            let checkpoint = store
6546                .get_checkpoint_by_sequence_number(*seq)?
6547                .and_then(|summary| {
6548                    store
6549                        .get_checkpoint_contents(&summary.content_digest)
6550                        .expect("db read cannot fail")
6551                });
6552            contents.push(checkpoint);
6553        }
6554
6555        let mut summaries_by_digest = Vec::with_capacity(checkpoint_summaries_by_digest.len());
6556        for digest in checkpoint_summaries_by_digest {
6557            let checkpoint = store
6558                .get_checkpoint_by_digest(digest)?
6559                .map(|c| c.into_inner());
6560            summaries_by_digest.push(checkpoint);
6561        }
6562        Ok((summaries, contents, summaries_by_digest))
6563    }
6564
6565    #[instrument(skip(self))]
6566    async fn deprecated_get_transaction_checkpoint(
6567        &self,
6568        digest: TransactionDigest,
6569    ) -> SuiResult<Option<CheckpointSequenceNumber>> {
6570        Ok(self
6571            .get_checkpoint_cache()
6572            .deprecated_get_transaction_checkpoint(&digest)
6573            .map(|(_epoch, checkpoint)| checkpoint))
6574    }
6575
6576    #[instrument(skip(self))]
6577    async fn get_object(
6578        &self,
6579        object_id: ObjectID,
6580        version: VersionNumber,
6581    ) -> SuiResult<Option<Object>> {
6582        Ok(self
6583            .get_object_cache_reader()
6584            .get_object_by_key(&object_id, version))
6585    }
6586
6587    #[instrument(skip_all)]
6588    async fn multi_get_objects(&self, object_keys: &[ObjectKey]) -> SuiResult<Vec<Option<Object>>> {
6589        Ok(self
6590            .get_object_cache_reader()
6591            .multi_get_objects_by_key(object_keys))
6592    }
6593
6594    #[instrument(skip(self))]
6595    async fn multi_get_transaction_checkpoint(
6596        &self,
6597        digests: &[TransactionDigest],
6598    ) -> SuiResult<Vec<Option<CheckpointSequenceNumber>>> {
6599        let res = self
6600            .get_checkpoint_cache()
6601            .deprecated_multi_get_transaction_checkpoint(digests);
6602
6603        Ok(res
6604            .into_iter()
6605            .map(|maybe| maybe.map(|(_epoch, checkpoint)| checkpoint))
6606            .collect())
6607    }
6608
6609    #[instrument(skip(self))]
6610    async fn multi_get_events_by_tx_digests(
6611        &self,
6612        digests: &[TransactionDigest],
6613    ) -> SuiResult<Vec<Option<TransactionEvents>>> {
6614        if digests.is_empty() {
6615            return Ok(vec![]);
6616        }
6617
6618        Ok(self
6619            .get_transaction_cache_reader()
6620            .multi_get_events(digests))
6621    }
6622}
6623
6624#[cfg(msim)]
6625pub mod framework_injection {
6626    use move_binary_format::CompiledModule;
6627    use std::collections::BTreeMap;
6628    use std::collections::BTreeSet;
6629    use std::sync::Mutex;
6630    use sui_framework::{BuiltInFramework, SystemPackage};
6631    use sui_types::base_types::{AuthorityName, ObjectID};
6632    use sui_types::is_system_package;
6633
6634    type FrameworkOverrideConfig = BTreeMap<ObjectID, PackageOverrideConfig>;
6635
6636    static OVERRIDE: Mutex<FrameworkOverrideConfig> = Mutex::new(BTreeMap::new());
6637
6638    type Framework = Vec<CompiledModule>;
6639
6640    pub type PackageUpgradeCallback =
6641        Box<dyn Fn(AuthorityName) -> Option<Framework> + Send + Sync + 'static>;
6642
6643    enum PackageOverrideConfig {
6644        Global(Framework),
6645        PerValidator(PackageUpgradeCallback),
6646    }
6647
6648    fn compiled_modules_to_bytes(modules: &[CompiledModule]) -> Vec<Vec<u8>> {
6649        modules
6650            .iter()
6651            .map(|m| {
6652                let mut buf = Vec::new();
6653                m.serialize_with_version(m.version, &mut buf).unwrap();
6654                buf
6655            })
6656            .collect()
6657    }
6658
6659    pub fn set_override(package_id: ObjectID, modules: Vec<CompiledModule>) {
6660        OVERRIDE
6661            .lock()
6662            .unwrap()
6663            .insert(package_id, PackageOverrideConfig::Global(modules));
6664    }
6665
6666    pub fn set_override_cb(package_id: ObjectID, func: PackageUpgradeCallback) {
6667        OVERRIDE
6668            .lock()
6669            .unwrap()
6670            .insert(package_id, PackageOverrideConfig::PerValidator(func));
6671    }
6672
6673    pub fn set_system_packages(packages: Vec<SystemPackage>) {
6674        let mut cfg = OVERRIDE.lock().unwrap();
6675        let mut new_packages_not_to_include: BTreeSet<_> =
6676            BuiltInFramework::all_package_ids().into_iter().collect();
6677        for pkg in &packages {
6678            new_packages_not_to_include.remove(&pkg.id);
6679        }
6680        for pkg in packages {
6681            cfg.insert(pkg.id, PackageOverrideConfig::Global(pkg.modules()));
6682        }
6683        for empty_pkg in new_packages_not_to_include {
6684            cfg.insert(empty_pkg, PackageOverrideConfig::Global(vec![]));
6685        }
6686    }
6687
6688    pub fn get_override_bytes(package_id: &ObjectID, name: AuthorityName) -> Option<Vec<Vec<u8>>> {
6689        OVERRIDE
6690            .lock()
6691            .unwrap()
6692            .get(package_id)
6693            .and_then(|entry| match entry {
6694                PackageOverrideConfig::Global(framework) => {
6695                    Some(compiled_modules_to_bytes(framework))
6696                }
6697                PackageOverrideConfig::PerValidator(func) => {
6698                    func(name).map(|fw| compiled_modules_to_bytes(&fw))
6699                }
6700            })
6701    }
6702
6703    pub fn get_override_modules(
6704        package_id: &ObjectID,
6705        name: AuthorityName,
6706    ) -> Option<Vec<CompiledModule>> {
6707        OVERRIDE
6708            .lock()
6709            .unwrap()
6710            .get(package_id)
6711            .and_then(|entry| match entry {
6712                PackageOverrideConfig::Global(framework) => Some(framework.clone()),
6713                PackageOverrideConfig::PerValidator(func) => func(name),
6714            })
6715    }
6716
6717    pub fn get_override_system_package(
6718        package_id: &ObjectID,
6719        name: AuthorityName,
6720    ) -> Option<SystemPackage> {
6721        let bytes = get_override_bytes(package_id, name)?;
6722        let dependencies = if is_system_package(*package_id) {
6723            BuiltInFramework::get_package_by_id(package_id)
6724                .dependencies
6725                .to_vec()
6726        } else {
6727            // Assume that entirely new injected packages depend on all existing system packages.
6728            BuiltInFramework::all_package_ids()
6729        };
6730        Some(SystemPackage {
6731            id: *package_id,
6732            bytes,
6733            dependencies,
6734        })
6735    }
6736
6737    pub fn get_extra_packages(name: AuthorityName) -> Vec<SystemPackage> {
6738        let built_in = BTreeSet::from_iter(BuiltInFramework::all_package_ids().into_iter());
6739        let extra: Vec<ObjectID> = OVERRIDE
6740            .lock()
6741            .unwrap()
6742            .keys()
6743            .filter_map(|package| (!built_in.contains(package)).then_some(*package))
6744            .collect();
6745
6746        extra
6747            .into_iter()
6748            .map(|package| SystemPackage {
6749                id: package,
6750                bytes: get_override_bytes(&package, name).unwrap(),
6751                dependencies: BuiltInFramework::all_package_ids(),
6752            })
6753            .collect()
6754    }
6755}
6756
6757#[derive(Debug, Serialize, Deserialize, Clone)]
6758pub struct ObjDumpFormat {
6759    pub id: ObjectID,
6760    pub version: VersionNumber,
6761    pub digest: ObjectDigest,
6762    pub object: Object,
6763}
6764
6765impl ObjDumpFormat {
6766    fn new(object: Object) -> Self {
6767        let oref = object.compute_object_reference();
6768        Self {
6769            id: oref.0,
6770            version: oref.1,
6771            digest: oref.2,
6772            object,
6773        }
6774    }
6775}
6776
6777#[derive(Debug, Serialize, Deserialize, Clone)]
6778pub struct NodeStateDump {
6779    pub tx_digest: TransactionDigest,
6780    pub sender_signed_data: SenderSignedData,
6781    pub executed_epoch: u64,
6782    pub reference_gas_price: u64,
6783    pub protocol_version: u64,
6784    pub epoch_start_timestamp_ms: u64,
6785    pub computed_effects: TransactionEffects,
6786    pub expected_effects_digest: TransactionEffectsDigest,
6787    pub relevant_system_packages: Vec<ObjDumpFormat>,
6788    pub shared_objects: Vec<ObjDumpFormat>,
6789    pub loaded_child_objects: Vec<ObjDumpFormat>,
6790    pub modified_at_versions: Vec<ObjDumpFormat>,
6791    pub runtime_reads: Vec<ObjDumpFormat>,
6792    pub input_objects: Vec<ObjDumpFormat>,
6793}
6794
6795impl NodeStateDump {
6796    pub fn new(
6797        tx_digest: &TransactionDigest,
6798        effects: &TransactionEffects,
6799        expected_effects_digest: TransactionEffectsDigest,
6800        object_store: &dyn ObjectStore,
6801        epoch_store: &Arc<AuthorityPerEpochStore>,
6802        inner_temporary_store: &InnerTemporaryStore,
6803        certificate: &VerifiedExecutableTransaction,
6804    ) -> SuiResult<Self> {
6805        // Epoch info
6806        let executed_epoch = epoch_store.epoch();
6807        let reference_gas_price = epoch_store.reference_gas_price();
6808        let epoch_start_config = epoch_store.epoch_start_config();
6809        let protocol_version = epoch_store.protocol_version().as_u64();
6810        let epoch_start_timestamp_ms = epoch_start_config.epoch_data().epoch_start_timestamp();
6811
6812        // Record all system packages at this version
6813        let mut relevant_system_packages = Vec::new();
6814        for sys_package_id in BuiltInFramework::all_package_ids() {
6815            if let Some(w) = object_store.get_object(&sys_package_id) {
6816                relevant_system_packages.push(ObjDumpFormat::new(w))
6817            }
6818        }
6819
6820        // Record all the shared objects
6821        let mut shared_objects = Vec::new();
6822        for kind in effects.input_consensus_objects() {
6823            match kind {
6824                InputConsensusObject::Mutate(obj_ref) | InputConsensusObject::ReadOnly(obj_ref) => {
6825                    if let Some(w) = object_store.get_object_by_key(&obj_ref.0, obj_ref.1) {
6826                        shared_objects.push(ObjDumpFormat::new(w))
6827                    }
6828                }
6829                InputConsensusObject::ReadConsensusStreamEnded(..)
6830                | InputConsensusObject::MutateConsensusStreamEnded(..)
6831                | InputConsensusObject::Cancelled(..) => (), // TODO: consider record congested objects.
6832            }
6833        }
6834
6835        // Record all loaded child objects
6836        // Child objects which are read but not mutated are not tracked anywhere else
6837        let mut loaded_child_objects = Vec::new();
6838        for (id, meta) in &inner_temporary_store.loaded_runtime_objects {
6839            if let Some(w) = object_store.get_object_by_key(id, meta.version) {
6840                loaded_child_objects.push(ObjDumpFormat::new(w))
6841            }
6842        }
6843
6844        // Record all modified objects
6845        let mut modified_at_versions = Vec::new();
6846        for (id, ver) in effects.modified_at_versions() {
6847            if let Some(w) = object_store.get_object_by_key(&id, ver) {
6848                modified_at_versions.push(ObjDumpFormat::new(w))
6849            }
6850        }
6851
6852        // Packages read at runtime, which were not previously loaded into the temoorary store
6853        // Some packages may be fetched at runtime and wont show up in input objects
6854        let mut runtime_reads = Vec::new();
6855        for obj in inner_temporary_store
6856            .runtime_packages_loaded_from_db
6857            .values()
6858        {
6859            runtime_reads.push(ObjDumpFormat::new(obj.object().clone()));
6860        }
6861
6862        // All other input objects should already be in `inner_temporary_store.objects`
6863
6864        Ok(Self {
6865            tx_digest: *tx_digest,
6866            executed_epoch,
6867            reference_gas_price,
6868            epoch_start_timestamp_ms,
6869            protocol_version,
6870            relevant_system_packages,
6871            shared_objects,
6872            loaded_child_objects,
6873            modified_at_versions,
6874            runtime_reads,
6875            sender_signed_data: certificate.clone().into_message(),
6876            input_objects: inner_temporary_store
6877                .input_objects
6878                .values()
6879                .map(|o| ObjDumpFormat::new(o.clone()))
6880                .collect(),
6881            computed_effects: effects.clone(),
6882            expected_effects_digest,
6883        })
6884    }
6885
6886    pub fn all_objects(&self) -> Vec<ObjDumpFormat> {
6887        let mut objects = Vec::new();
6888        objects.extend(self.relevant_system_packages.clone());
6889        objects.extend(self.shared_objects.clone());
6890        objects.extend(self.loaded_child_objects.clone());
6891        objects.extend(self.modified_at_versions.clone());
6892        objects.extend(self.runtime_reads.clone());
6893        objects.extend(self.input_objects.clone());
6894        objects
6895    }
6896
6897    pub fn write_to_file(&self, path: &Path) -> Result<PathBuf, anyhow::Error> {
6898        let file_name = format!(
6899            "{}_{}_NODE_DUMP.json",
6900            self.tx_digest,
6901            AuthorityState::unixtime_now_ms()
6902        );
6903        let mut path = path.to_path_buf();
6904        path.push(&file_name);
6905        let mut file = File::create(path.clone())?;
6906        file.write_all(serde_json::to_string_pretty(self)?.as_bytes())?;
6907        Ok(path)
6908    }
6909
6910    pub fn read_from_file(path: &PathBuf) -> Result<Self, anyhow::Error> {
6911        let file = File::open(path)?;
6912        serde_json::from_reader(file).map_err(|e| anyhow::anyhow!(e))
6913    }
6914}