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