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