Skip to main content

sui_rpc_api/grpc/v2/transaction_execution_service/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::ErrorReason;
5use crate::RpcError;
6use crate::RpcService;
7use prost_types::FieldMask;
8use sui_rpc::field::FieldMaskTree;
9use sui_rpc::field::FieldMaskUtil;
10use sui_rpc::merge::Merge;
11use sui_rpc::proto::google::rpc::bad_request::FieldViolation;
12use sui_rpc::proto::sui::rpc::v2::ExecuteTransactionRequest;
13use sui_rpc::proto::sui::rpc::v2::ExecuteTransactionResponse;
14use sui_rpc::proto::sui::rpc::v2::ExecutedTransaction;
15use sui_rpc::proto::sui::rpc::v2::ObjectSet;
16use sui_rpc::proto::sui::rpc::v2::SimulateTransactionRequest;
17use sui_rpc::proto::sui::rpc::v2::SimulateTransactionResponse;
18use sui_rpc::proto::sui::rpc::v2::Transaction;
19use sui_rpc::proto::sui::rpc::v2::UserSignature;
20use sui_rpc::proto::sui::rpc::v2::transaction_execution_service_server::TransactionExecutionService;
21use sui_types::balance_change::derive_balance_changes_2;
22use sui_types::transaction_executor::TransactionExecutor;
23use tap::Pipe;
24
25mod simulate;
26
27#[tonic::async_trait]
28impl TransactionExecutionService for RpcService {
29    async fn execute_transaction(
30        &self,
31        request: tonic::Request<ExecuteTransactionRequest>,
32    ) -> Result<tonic::Response<ExecuteTransactionResponse>, tonic::Status> {
33        let executor = self
34            .executor
35            .as_ref()
36            .ok_or_else(|| tonic::Status::unimplemented("no transaction executor"))?;
37
38        execute_transaction(self, executor, request.into_inner())
39            .await
40            .map(tonic::Response::new)
41            .map_err(Into::into)
42    }
43
44    async fn simulate_transaction(
45        &self,
46        request: tonic::Request<SimulateTransactionRequest>,
47    ) -> Result<tonic::Response<SimulateTransactionResponse>, tonic::Status> {
48        let service = self.clone();
49        if let Some(metrics) = &self.metrics {
50            metrics.observe_simulate_client_protocol_version(
51                crate::client_protocol_version::client_protocol_version_for_metrics(
52                    request.metadata(),
53                ),
54            );
55        }
56        let client_protocol_version = crate::client_protocol_version(request.metadata());
57        let request = request.into_inner();
58        tokio::task::spawn_blocking(move || {
59            simulate::simulate_transaction(&service, request, client_protocol_version)
60        })
61        .await
62        .map_err(|e| tonic::Status::internal(format!("simulate_transaction task failed: {e}")))?
63        .map(tonic::Response::new)
64        .map_err(Into::into)
65    }
66}
67
68pub const EXECUTE_TRANSACTION_READ_MASK_DEFAULT: &str =
69    crate::read_mask_defaults::EXECUTE_TRANSACTION;
70// Current maximum number of supported UserSignature's,
71// one for the sender and one for an optional sponsor
72const MAX_NUMBER_OF_SIGNATURES: usize = 2;
73
74#[tracing::instrument(skip(service, executor))]
75pub async fn execute_transaction(
76    service: &RpcService,
77    executor: &std::sync::Arc<dyn TransactionExecutor>,
78    request: ExecuteTransactionRequest,
79) -> Result<ExecuteTransactionResponse, RpcError> {
80    let transaction = request
81        .transaction
82        .as_ref()
83        .ok_or_else(|| FieldViolation::new("transaction").with_reason(ErrorReason::FieldMissing))?
84        .pipe(sui_sdk_types::Transaction::try_from)
85        .map_err(|e| {
86            FieldViolation::new("transaction")
87                .with_description(format!("invalid transaction: {e}"))
88                .with_reason(ErrorReason::FieldInvalid)
89        })?;
90
91    if request.signatures.len() > MAX_NUMBER_OF_SIGNATURES {
92        return Err(FieldViolation::new("signatures")
93            .with_description(format!(
94                "{} provided signatures exceeds the maximum allowed of {}",
95                request.signatures.len(),
96                MAX_NUMBER_OF_SIGNATURES
97            ))
98            .with_reason(ErrorReason::FieldInvalid)
99            .into());
100    }
101
102    let signatures = request
103        .signatures
104        .iter()
105        .enumerate()
106        .map(|(i, signature)| {
107            sui_sdk_types::UserSignature::try_from(signature).map_err(|e| {
108                FieldViolation::new_at("signatures", i)
109                    .with_description(format!("invalid signature: {e}"))
110                    .with_reason(ErrorReason::FieldInvalid)
111            })
112        })
113        .collect::<Result<Vec<_>, _>>()?;
114
115    let signed_transaction = sui_sdk_types::SignedTransaction {
116        transaction: transaction.clone(),
117        signatures: signatures.clone(),
118    };
119
120    let read_mask = {
121        let read_mask = request
122            .read_mask
123            .unwrap_or_else(|| FieldMask::from_str(EXECUTE_TRANSACTION_READ_MASK_DEFAULT));
124        read_mask
125            .validate::<ExecutedTransaction>()
126            .map_err(|path| {
127                FieldViolation::new("read_mask")
128                    .with_description(format!("invalid read_mask path: {path}"))
129                    .with_reason(ErrorReason::FieldInvalid)
130            })?;
131        FieldMaskTree::from(read_mask)
132    };
133
134    let request = sui_types::transaction_driver_types::ExecuteTransactionRequestV3 {
135        transaction: signed_transaction.try_into()?,
136        include_events: read_mask.contains(ExecutedTransaction::EVENTS_FIELD.name),
137        include_input_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
138            || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
139            || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
140        include_output_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
141            || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
142            || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
143        include_auxiliary_data: false,
144    };
145
146    let sui_types::transaction_driver_types::ExecuteTransactionResponseV3 {
147        effects:
148            sui_types::transaction_driver_types::FinalizedEffects {
149                effects,
150                finality_info: _,
151            },
152        events,
153        input_objects,
154        output_objects,
155        auxiliary_data: _,
156    } = executor.execute_transaction(request, None).await?;
157
158    let executed_transaction = {
159        // Build the objects set first so we can use it for event JSON rendering.
160        // This allows resolving types from packages that were just published in this transaction.
161        let objects = {
162            let mut objects = sui_types::full_checkpoint_content::ObjectSet::default();
163            for o in input_objects
164                .into_iter()
165                .chain(output_objects.into_iter())
166                .flatten()
167            {
168                objects.insert(o);
169            }
170            objects
171        };
172
173        let events = read_mask
174            .subtree(ExecutedTransaction::EVENTS_FIELD)
175            .and_then(|mask| {
176                events.map(|events| service.render_events_to_proto(&events, &mask, &objects))
177            });
178
179        let balance_changes = if read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD) {
180            derive_balance_changes_2(&effects, &objects)
181                .into_iter()
182                .map(Into::into)
183                .collect()
184        } else {
185            vec![]
186        };
187
188        let effects = read_mask
189            .subtree(ExecutedTransaction::EFFECTS_FIELD)
190            .map(|mask| service.render_effects_to_proto(&effects, &[], &objects, &mask));
191
192        let mut message = ExecutedTransaction::default();
193        message.digest = read_mask
194            .contains(ExecutedTransaction::DIGEST_FIELD)
195            .then(|| transaction.digest().to_string());
196        message.transaction = read_mask
197            .subtree(ExecutedTransaction::TRANSACTION_FIELD)
198            .map(|mask| Transaction::merge_from(transaction, &mask));
199        message.signatures = read_mask
200            .subtree(ExecutedTransaction::SIGNATURES_FIELD)
201            .map(|mask| {
202                signatures
203                    .into_iter()
204                    .map(|s| UserSignature::merge_from(s, &mask))
205                    .collect()
206            })
207            .unwrap_or_default();
208        message.effects = effects;
209        message.events = events;
210        message.balance_changes = balance_changes;
211        message.objects = read_mask
212            .subtree(
213                ExecutedTransaction::path_builder()
214                    .objects()
215                    .objects()
216                    .finish(),
217            )
218            .map(|mask| {
219                ObjectSet::default().with_objects(
220                    objects
221                        .iter()
222                        .map(|o| service.render_object_to_proto(o, &mask, &objects))
223                        .collect(),
224                )
225            });
226        message
227    };
228
229    Ok(ExecuteTransactionResponse::default().with_transaction(executed_transaction))
230}