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::Object;
16use sui_rpc::proto::sui::rpc::v2::ObjectSet;
17use sui_rpc::proto::sui::rpc::v2::SimulateTransactionRequest;
18use sui_rpc::proto::sui::rpc::v2::SimulateTransactionResponse;
19use sui_rpc::proto::sui::rpc::v2::Transaction;
20use sui_rpc::proto::sui::rpc::v2::TransactionEffects;
21use sui_rpc::proto::sui::rpc::v2::TransactionEvents;
22use sui_rpc::proto::sui::rpc::v2::UserSignature;
23use sui_rpc::proto::sui::rpc::v2::transaction_execution_service_server::TransactionExecutionService;
24use sui_sdk_types::Address;
25use sui_types::balance_change::derive_balance_changes;
26use sui_types::transaction_executor::TransactionExecutor;
27use tap::Pipe;
28
29mod simulate;
30
31#[tonic::async_trait]
32impl TransactionExecutionService for RpcService {
33    async fn execute_transaction(
34        &self,
35        request: tonic::Request<ExecuteTransactionRequest>,
36    ) -> Result<tonic::Response<ExecuteTransactionResponse>, tonic::Status> {
37        let executor = self
38            .executor
39            .as_ref()
40            .ok_or_else(|| tonic::Status::unimplemented("no transaction executor"))?;
41
42        execute_transaction(self, executor, request.into_inner())
43            .await
44            .map(tonic::Response::new)
45            .map_err(Into::into)
46    }
47
48    async fn simulate_transaction(
49        &self,
50        request: tonic::Request<SimulateTransactionRequest>,
51    ) -> Result<tonic::Response<SimulateTransactionResponse>, tonic::Status> {
52        simulate::simulate_transaction(self, request.into_inner())
53            .map(tonic::Response::new)
54            .map_err(Into::into)
55    }
56}
57
58pub const EXECUTE_TRANSACTION_READ_MASK_DEFAULT: &str = "effects";
59
60#[tracing::instrument(skip(service, executor))]
61pub async fn execute_transaction(
62    service: &RpcService,
63    executor: &std::sync::Arc<dyn TransactionExecutor>,
64    request: ExecuteTransactionRequest,
65) -> Result<ExecuteTransactionResponse, RpcError> {
66    let transaction = request
67        .transaction
68        .as_ref()
69        .ok_or_else(|| FieldViolation::new("transaction").with_reason(ErrorReason::FieldMissing))?
70        .pipe(sui_sdk_types::Transaction::try_from)
71        .map_err(|e| {
72            FieldViolation::new("transaction")
73                .with_description(format!("invalid transaction: {e}"))
74                .with_reason(ErrorReason::FieldInvalid)
75        })?;
76
77    let signatures = request
78        .signatures
79        .iter()
80        .enumerate()
81        .map(|(i, signature)| {
82            sui_sdk_types::UserSignature::try_from(signature).map_err(|e| {
83                FieldViolation::new_at("signatures", i)
84                    .with_description(format!("invalid signature: {e}"))
85                    .with_reason(ErrorReason::FieldInvalid)
86            })
87        })
88        .collect::<Result<Vec<_>, _>>()?;
89
90    let signed_transaction = sui_sdk_types::SignedTransaction {
91        transaction: transaction.clone(),
92        signatures: signatures.clone(),
93    };
94
95    let read_mask = {
96        let read_mask = request
97            .read_mask
98            .unwrap_or_else(|| FieldMask::from_str(EXECUTE_TRANSACTION_READ_MASK_DEFAULT));
99        read_mask
100            .validate::<ExecutedTransaction>()
101            .map_err(|path| {
102                FieldViolation::new("read_mask")
103                    .with_description(format!("invalid read_mask path: {path}"))
104                    .with_reason(ErrorReason::FieldInvalid)
105            })?;
106        FieldMaskTree::from(read_mask)
107    };
108
109    let request = sui_types::quorum_driver_types::ExecuteTransactionRequestV3 {
110        transaction: signed_transaction.try_into()?,
111        include_events: read_mask.contains(ExecutedTransaction::EVENTS_FIELD.name),
112        include_input_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
113            || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
114            || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
115        include_output_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
116            || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
117            || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
118        include_auxiliary_data: false,
119    };
120
121    let sui_types::quorum_driver_types::ExecuteTransactionResponseV3 {
122        effects:
123            sui_types::quorum_driver_types::FinalizedEffects {
124                effects,
125                finality_info: _,
126            },
127        events,
128        input_objects,
129        output_objects,
130        auxiliary_data: _,
131    } = executor.execute_transaction(request, None).await?;
132
133    let executed_transaction = {
134        let events = read_mask
135            .subtree(ExecutedTransaction::EVENTS_FIELD)
136            .and_then(|mask| events.map(|e| TransactionEvents::merge_from(&e, &mask)));
137
138        let input_objects = input_objects.unwrap_or_default();
139        let output_objects = output_objects.unwrap_or_default();
140
141        let balance_changes = if read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
142        {
143            derive_balance_changes(&effects, &input_objects, &output_objects)
144                .into_iter()
145                .map(Into::into)
146                .collect()
147        } else {
148            vec![]
149        };
150
151        let input_objects = input_objects
152            .into_iter()
153            .map(sui_sdk_types::Object::try_from)
154            .collect::<Result<Vec<_>, _>>()?;
155        let output_objects = output_objects
156            .into_iter()
157            .map(sui_sdk_types::Object::try_from)
158            .collect::<Result<Vec<_>, _>>()?;
159
160        let effects = sui_sdk_types::TransactionEffects::try_from(effects)?;
161        let effects = read_mask
162            .subtree(ExecutedTransaction::EFFECTS_FIELD.name)
163            .map(|mask| {
164                let mut effects = TransactionEffects::merge_from(&effects, &mask);
165
166                if mask.contains(TransactionEffects::CHANGED_OBJECTS_FIELD.name) {
167                    for changed_object in effects.changed_objects.iter_mut() {
168                        let Ok(object_id) = changed_object.object_id().parse::<Address>() else {
169                            continue;
170                        };
171
172                        if let Some(object) = input_objects
173                            .iter()
174                            .chain(&output_objects)
175                            .find(|o| o.object_id() == object_id)
176                        {
177                            changed_object.object_type = Some(match object.object_type() {
178                                sui_sdk_types::ObjectType::Package => "package".to_owned(),
179                                sui_sdk_types::ObjectType::Struct(struct_tag) => {
180                                    struct_tag.to_string()
181                                }
182                            });
183                        }
184                    }
185                }
186
187                if mask.contains(TransactionEffects::UNCHANGED_CONSENSUS_OBJECTS_FIELD.name) {
188                    for unchanged_consensus_object in effects.unchanged_consensus_objects.iter_mut()
189                    {
190                        let Ok(object_id) =
191                            unchanged_consensus_object.object_id().parse::<Address>()
192                        else {
193                            continue;
194                        };
195
196                        if let Some(object) =
197                            input_objects.iter().find(|o| o.object_id() == object_id)
198                        {
199                            unchanged_consensus_object.object_type =
200                                Some(match object.object_type() {
201                                    sui_sdk_types::ObjectType::Package => "package".to_owned(),
202                                    sui_sdk_types::ObjectType::Struct(struct_tag) => {
203                                        struct_tag.to_string()
204                                    }
205                                });
206                        }
207                    }
208                }
209
210                // Try to render clever error info
211                super::ledger_service::render_clever_error(service, &mut effects);
212
213                effects
214            });
215
216        let mut message = ExecutedTransaction::default();
217        message.digest = read_mask
218            .contains(ExecutedTransaction::DIGEST_FIELD.name)
219            .then(|| transaction.digest().to_string());
220        message.transaction = read_mask
221            .subtree(ExecutedTransaction::TRANSACTION_FIELD.name)
222            .map(|mask| Transaction::merge_from(transaction, &mask));
223        message.signatures = read_mask
224            .subtree(ExecutedTransaction::SIGNATURES_FIELD.name)
225            .map(|mask| {
226                signatures
227                    .into_iter()
228                    .map(|s| UserSignature::merge_from(s, &mask))
229                    .collect()
230            })
231            .unwrap_or_default();
232        message.effects = effects;
233        message.events = events;
234        message.balance_changes = balance_changes;
235        message.objects = read_mask
236            .subtree(
237                ExecutedTransaction::path_builder()
238                    .objects()
239                    .objects()
240                    .finish(),
241            )
242            .map(|mask| {
243                let set: std::collections::BTreeMap<_, _> = input_objects
244                    .into_iter()
245                    .chain(output_objects.into_iter())
246                    .map(|object| ((object.object_id(), object.version()), object))
247                    .collect();
248                ObjectSet::default().with_objects(
249                    set.into_values()
250                        .map(|o| Object::merge_from(o, &mask))
251                        .collect(),
252                )
253            });
254        message
255    };
256
257    Ok(ExecuteTransactionResponse::default().with_transaction(executed_transaction))
258}