sui_json_rpc/
transaction_execution_api.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::sync::Arc;
5use std::time::Duration;
6
7use async_trait::async_trait;
8use fastcrypto::encoding::Base64;
9use fastcrypto::traits::ToFromBytes;
10use jsonrpsee::RpcModule;
11use jsonrpsee::core::RpcResult;
12
13use crate::authority_state::StateRead;
14use crate::error::{Error, SuiRpcInputError};
15use crate::{
16    ObjectProviderCache, SuiRpcModule, get_balance_changes_from_effect, get_object_changes,
17    with_tracing,
18};
19use shared_crypto::intent::{AppId, Intent, IntentMessage, IntentScope, IntentVersion};
20use sui_core::authority::AuthorityState;
21use sui_core::authority_client::NetworkAuthorityClient;
22use sui_core::transaction_orchestrator::TransactionOrchestrator;
23use sui_json_rpc_api::{JsonRpcMetrics, WriteApiOpenRpc, WriteApiServer};
24use sui_json_rpc_types::{
25    DevInspectArgs, DevInspectResults, DryRunTransactionBlockResponse, SuiTransactionBlock,
26    SuiTransactionBlockEvents, SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions,
27};
28use sui_open_rpc::Module;
29use sui_types::base_types::SuiAddress;
30use sui_types::crypto::default_hash;
31use sui_types::digests::TransactionDigest;
32use sui_types::effects::TransactionEffectsAPI;
33use sui_types::signature::GenericSignature;
34use sui_types::storage::PostExecutionPackageResolver;
35use sui_types::sui_serde::BigInt;
36use sui_types::transaction::{
37    InputObjectKind, Transaction, TransactionData, TransactionDataAPI, TransactionKind,
38};
39use sui_types::transaction_driver_types::{
40    ExecuteTransactionRequestType, ExecuteTransactionRequestV3, ExecuteTransactionResponseV3,
41};
42use tracing::instrument;
43
44pub struct TransactionExecutionApi {
45    state: Arc<dyn StateRead>,
46    transaction_orchestrator: Arc<TransactionOrchestrator<NetworkAuthorityClient>>,
47    metrics: Arc<JsonRpcMetrics>,
48}
49
50impl TransactionExecutionApi {
51    pub fn new(
52        state: Arc<AuthorityState>,
53        transaction_orchestrator: Arc<TransactionOrchestrator<NetworkAuthorityClient>>,
54        metrics: Arc<JsonRpcMetrics>,
55    ) -> Self {
56        Self {
57            state,
58            transaction_orchestrator,
59            metrics,
60        }
61    }
62
63    pub fn convert_bytes<T: serde::de::DeserializeOwned>(
64        &self,
65        tx_bytes: Base64,
66    ) -> Result<T, SuiRpcInputError> {
67        let data: T = bcs::from_bytes(&tx_bytes.to_vec()?)?;
68        Ok(data)
69    }
70
71    #[allow(clippy::type_complexity)]
72    fn prepare_execute_transaction_block(
73        &self,
74        tx_bytes: Base64,
75        signatures: Vec<Base64>,
76        opts: Option<SuiTransactionBlockResponseOptions>,
77    ) -> Result<
78        (
79            ExecuteTransactionRequestV3,
80            SuiTransactionBlockResponseOptions,
81            SuiAddress,
82            Vec<InputObjectKind>,
83            Transaction,
84            Option<SuiTransactionBlock>,
85            Vec<u8>,
86        ),
87        SuiRpcInputError,
88    > {
89        let opts = opts.unwrap_or_default();
90
91        let tx_data: TransactionData = self.convert_bytes(tx_bytes)?;
92        let sender = tx_data.sender();
93        let input_objs = tx_data.input_objects().unwrap_or_default();
94
95        let mut sigs = Vec::new();
96        for sig in signatures {
97            sigs.push(GenericSignature::from_bytes(&sig.to_vec()?)?);
98        }
99        let txn = Transaction::from_generic_sig_data(tx_data, sigs);
100        let raw_transaction = if opts.show_raw_input {
101            bcs::to_bytes(txn.data())?
102        } else {
103            vec![]
104        };
105        let transaction = if opts.show_input {
106            let epoch_store = self.state.load_epoch_store_one_call_per_task();
107            Some(SuiTransactionBlock::try_from(
108                txn.data().clone(),
109                epoch_store.module_cache(),
110            )?)
111        } else {
112            None
113        };
114
115        let request = ExecuteTransactionRequestV3 {
116            transaction: txn.clone(),
117            include_events: opts.show_events,
118            include_input_objects: opts.show_balance_changes || opts.show_object_changes,
119            include_output_objects: opts.show_balance_changes
120                || opts.show_object_changes
121                // In order to resolve events, we may need access to the newly published packages.
122                || opts.show_events,
123            include_auxiliary_data: false,
124        };
125
126        Ok((
127            request,
128            opts,
129            sender,
130            input_objs,
131            txn,
132            transaction,
133            raw_transaction,
134        ))
135    }
136
137    async fn execute_transaction_block(
138        &self,
139        tx_bytes: Base64,
140        signatures: Vec<Base64>,
141        opts: Option<SuiTransactionBlockResponseOptions>,
142        request_type: Option<ExecuteTransactionRequestType>,
143    ) -> Result<SuiTransactionBlockResponse, Error> {
144        let request_type =
145            request_type.unwrap_or(ExecuteTransactionRequestType::WaitForEffectsCert);
146        let (request, opts, sender, input_objs, txn, transaction, raw_transaction) =
147            self.prepare_execute_transaction_block(tx_bytes, signatures, opts)?;
148        let digest = *txn.digest();
149
150        let transaction_orchestrator = self.transaction_orchestrator.clone();
151        let orch_timer = self.metrics.orchestrator_latency_ms.start_timer();
152        let (response, is_executed_locally) = transaction_orchestrator
153            .execute_transaction_block(request, request_type, None)
154            .await
155            .map_err(Error::from)?;
156        drop(orch_timer);
157
158        self.handle_post_orchestration(
159            response,
160            is_executed_locally,
161            opts,
162            digest,
163            input_objs,
164            transaction,
165            raw_transaction,
166            sender,
167        )
168        .await
169    }
170
171    async fn handle_post_orchestration(
172        &self,
173        response: ExecuteTransactionResponseV3,
174        is_executed_locally: bool,
175        opts: SuiTransactionBlockResponseOptions,
176        digest: TransactionDigest,
177        input_objs: Vec<InputObjectKind>,
178        transaction: Option<SuiTransactionBlock>,
179        raw_transaction: Vec<u8>,
180        sender: SuiAddress,
181    ) -> Result<SuiTransactionBlockResponse, Error> {
182        let _post_orch_timer = self.metrics.post_orchestrator_latency_ms.start_timer();
183
184        let events = if opts.show_events {
185            let epoch_store = self.state.load_epoch_store_one_call_per_task();
186            let backing_package_store = PostExecutionPackageResolver::new(
187                self.state.get_backing_package_store().clone(),
188                &response.output_objects,
189            );
190            let mut layout_resolver = epoch_store
191                .executor()
192                .type_layout_resolver(Box::new(backing_package_store));
193            Some(SuiTransactionBlockEvents::try_from(
194                response.events.unwrap_or_default(),
195                digest,
196                None,
197                layout_resolver.as_mut(),
198            )?)
199        } else {
200            None
201        };
202
203        let object_cache = if opts.show_balance_changes || opts.show_object_changes {
204            let mut object_cache = ObjectProviderCache::new(self.state.clone());
205            if let Some(input_objects) = response.input_objects {
206                object_cache.insert_objects_into_cache(input_objects);
207            }
208            if let Some(output_objects) = response.output_objects {
209                object_cache.insert_objects_into_cache(output_objects);
210            }
211            Some(object_cache)
212        } else {
213            None
214        };
215
216        let balance_changes = match &object_cache {
217            Some(object_cache) if opts.show_balance_changes => Some(
218                get_balance_changes_from_effect(
219                    object_cache,
220                    &response.effects.effects,
221                    input_objs,
222                    None,
223                )
224                .await?,
225            ),
226            _ => None,
227        };
228
229        let object_changes = match &object_cache {
230            Some(object_cache) if opts.show_object_changes => Some(
231                get_object_changes(
232                    object_cache,
233                    &response.effects.effects,
234                    sender,
235                    response.effects.effects.modified_at_versions(),
236                    response.effects.effects.all_changed_objects(),
237                    response.effects.effects.all_removed_objects(),
238                )
239                .await?,
240            ),
241            _ => None,
242        };
243
244        let raw_effects = if opts.show_raw_effects {
245            bcs::to_bytes(&response.effects.effects)?
246        } else {
247            vec![]
248        };
249
250        Ok(SuiTransactionBlockResponse {
251            digest,
252            transaction,
253            raw_transaction,
254            effects: opts
255                .show_effects
256                .then_some(response.effects.effects.try_into()?),
257            events,
258            object_changes,
259            balance_changes,
260            timestamp_ms: None,
261            confirmed_local_execution: Some(is_executed_locally),
262            checkpoint: None,
263            errors: vec![],
264            raw_effects,
265        })
266    }
267
268    pub fn prepare_dry_run_transaction_block(
269        &self,
270        tx_bytes: Base64,
271    ) -> Result<(TransactionData, TransactionDigest, Vec<InputObjectKind>), SuiRpcInputError> {
272        let tx_data: TransactionData = self.convert_bytes(tx_bytes)?;
273        let input_objs = tx_data.input_objects()?;
274        let intent_msg = IntentMessage::new(
275            Intent {
276                version: IntentVersion::V0,
277                scope: IntentScope::TransactionData,
278                app_id: AppId::Sui,
279            },
280            tx_data,
281        );
282        let txn_digest = TransactionDigest::new(default_hash(&intent_msg.value));
283        Ok((intent_msg.value, txn_digest, input_objs))
284    }
285
286    async fn dry_run_transaction_block(
287        &self,
288        tx_bytes: Base64,
289    ) -> Result<DryRunTransactionBlockResponse, Error> {
290        let (txn_data, txn_digest, input_objs) =
291            self.prepare_dry_run_transaction_block(tx_bytes)?;
292        let sender = txn_data.sender();
293        let (resp, written_objects, transaction_effects, mock_gas) = self
294            .state
295            .dry_exec_transaction(txn_data.clone(), txn_digest)
296            .await?;
297        let object_cache = ObjectProviderCache::new_with_cache(self.state.clone(), written_objects);
298        let balance_changes = get_balance_changes_from_effect(
299            &object_cache,
300            &transaction_effects,
301            input_objs,
302            mock_gas,
303        )
304        .await?;
305        let object_changes = get_object_changes(
306            &object_cache,
307            &transaction_effects,
308            sender,
309            transaction_effects.modified_at_versions(),
310            transaction_effects.all_changed_objects(),
311            transaction_effects.all_removed_objects(),
312        )
313        .await?;
314
315        Ok(DryRunTransactionBlockResponse {
316            effects: resp.effects,
317            events: resp.events,
318            object_changes,
319            balance_changes,
320            input: resp.input,
321            execution_error_source: resp.execution_error_source,
322            suggested_gas_price: resp.suggested_gas_price,
323        })
324    }
325}
326
327#[async_trait]
328impl WriteApiServer for TransactionExecutionApi {
329    #[instrument(skip_all)]
330    async fn execute_transaction_block(
331        &self,
332        tx_bytes: Base64,
333        signatures: Vec<Base64>,
334        opts: Option<SuiTransactionBlockResponseOptions>,
335        request_type: Option<ExecuteTransactionRequestType>,
336    ) -> RpcResult<SuiTransactionBlockResponse> {
337        with_tracing!(Duration::from_secs(10), async move {
338            self.execute_transaction_block(tx_bytes, signatures, opts, request_type)
339                .await
340        })
341    }
342
343    #[instrument(skip(self))]
344    async fn dev_inspect_transaction_block(
345        &self,
346        sender_address: SuiAddress,
347        tx_bytes: Base64,
348        gas_price: Option<BigInt<u64>>,
349        _epoch: Option<BigInt<u64>>,
350        additional_args: Option<DevInspectArgs>,
351    ) -> RpcResult<DevInspectResults> {
352        with_tracing!(async move {
353            let DevInspectArgs {
354                gas_sponsor,
355                gas_budget,
356                gas_objects,
357                show_raw_txn_data_and_effects,
358                skip_checks,
359            } = additional_args.unwrap_or_default();
360            let tx_kind: TransactionKind = self.convert_bytes(tx_bytes)?;
361            self.state
362                .dev_inspect_transaction_block(
363                    sender_address,
364                    tx_kind,
365                    gas_price.map(|i| *i),
366                    gas_budget.map(|i| *i),
367                    gas_sponsor,
368                    gas_objects,
369                    show_raw_txn_data_and_effects,
370                    skip_checks,
371                )
372                .await
373                .map_err(Error::from)
374        })
375    }
376
377    #[instrument(skip(self))]
378    async fn dry_run_transaction_block(
379        &self,
380        tx_bytes: Base64,
381    ) -> RpcResult<DryRunTransactionBlockResponse> {
382        with_tracing!(async move { self.dry_run_transaction_block(tx_bytes).await })
383    }
384}
385
386impl SuiRpcModule for TransactionExecutionApi {
387    fn rpc(self) -> RpcModule<Self> {
388        self.into_rpc()
389    }
390
391    fn rpc_doc_module() -> Module {
392        WriteApiOpenRpc::module_doc()
393    }
394}