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        simulate::simulate_transaction(self, request.into_inner())
49            .map(tonic::Response::new)
50            .map_err(Into::into)
51    }
52}
53
54pub const EXECUTE_TRANSACTION_READ_MASK_DEFAULT: &str = "effects";
55
56#[tracing::instrument(skip(service, executor))]
57pub async fn execute_transaction(
58    service: &RpcService,
59    executor: &std::sync::Arc<dyn TransactionExecutor>,
60    request: ExecuteTransactionRequest,
61) -> Result<ExecuteTransactionResponse, RpcError> {
62    let transaction = request
63        .transaction
64        .as_ref()
65        .ok_or_else(|| FieldViolation::new("transaction").with_reason(ErrorReason::FieldMissing))?
66        .pipe(sui_sdk_types::Transaction::try_from)
67        .map_err(|e| {
68            FieldViolation::new("transaction")
69                .with_description(format!("invalid transaction: {e}"))
70                .with_reason(ErrorReason::FieldInvalid)
71        })?;
72
73    let signatures = request
74        .signatures
75        .iter()
76        .enumerate()
77        .map(|(i, signature)| {
78            sui_sdk_types::UserSignature::try_from(signature).map_err(|e| {
79                FieldViolation::new_at("signatures", i)
80                    .with_description(format!("invalid signature: {e}"))
81                    .with_reason(ErrorReason::FieldInvalid)
82            })
83        })
84        .collect::<Result<Vec<_>, _>>()?;
85
86    let signed_transaction = sui_sdk_types::SignedTransaction {
87        transaction: transaction.clone(),
88        signatures: signatures.clone(),
89    };
90
91    let read_mask = {
92        let read_mask = request
93            .read_mask
94            .unwrap_or_else(|| FieldMask::from_str(EXECUTE_TRANSACTION_READ_MASK_DEFAULT));
95        read_mask
96            .validate::<ExecutedTransaction>()
97            .map_err(|path| {
98                FieldViolation::new("read_mask")
99                    .with_description(format!("invalid read_mask path: {path}"))
100                    .with_reason(ErrorReason::FieldInvalid)
101            })?;
102        FieldMaskTree::from(read_mask)
103    };
104
105    let request = sui_types::quorum_driver_types::ExecuteTransactionRequestV3 {
106        transaction: signed_transaction.try_into()?,
107        include_events: read_mask.contains(ExecutedTransaction::EVENTS_FIELD.name),
108        include_input_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
109            || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
110            || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
111        include_output_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
112            || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
113            || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
114        include_auxiliary_data: false,
115    };
116
117    let sui_types::quorum_driver_types::ExecuteTransactionResponseV3 {
118        effects:
119            sui_types::quorum_driver_types::FinalizedEffects {
120                effects,
121                finality_info: _,
122            },
123        events,
124        input_objects,
125        output_objects,
126        auxiliary_data: _,
127    } = executor.execute_transaction(request, None).await?;
128
129    let executed_transaction = {
130        let events = read_mask
131            .subtree(ExecutedTransaction::EVENTS_FIELD)
132            .and_then(|mask| events.map(|events| service.render_events_to_proto(&events, &mask)));
133
134        let objects = {
135            let mut objects = sui_types::full_checkpoint_content::ObjectSet::default();
136            for o in input_objects
137                .into_iter()
138                .chain(output_objects.into_iter())
139                .flatten()
140            {
141                objects.insert(o);
142            }
143            objects
144        };
145
146        let balance_changes = if read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD) {
147            derive_balance_changes_2(&effects, &objects)
148                .into_iter()
149                .map(Into::into)
150                .collect()
151        } else {
152            vec![]
153        };
154
155        let effects = read_mask
156            .subtree(ExecutedTransaction::EFFECTS_FIELD)
157            .map(|mask| {
158                service.render_effects_to_proto(
159                    &effects,
160                    &[],
161                    |object_id| {
162                        objects
163                            .iter()
164                            .find(|o| o.id() == *object_id)
165                            .map(|o| o.into())
166                    },
167                    &mask,
168                )
169            });
170
171        let mut message = ExecutedTransaction::default();
172        message.digest = read_mask
173            .contains(ExecutedTransaction::DIGEST_FIELD)
174            .then(|| transaction.digest().to_string());
175        message.transaction = read_mask
176            .subtree(ExecutedTransaction::TRANSACTION_FIELD)
177            .map(|mask| Transaction::merge_from(transaction, &mask));
178        message.signatures = read_mask
179            .subtree(ExecutedTransaction::SIGNATURES_FIELD)
180            .map(|mask| {
181                signatures
182                    .into_iter()
183                    .map(|s| UserSignature::merge_from(s, &mask))
184                    .collect()
185            })
186            .unwrap_or_default();
187        message.effects = effects;
188        message.events = events;
189        message.balance_changes = balance_changes;
190        message.objects = read_mask
191            .subtree(
192                ExecutedTransaction::path_builder()
193                    .objects()
194                    .objects()
195                    .finish(),
196            )
197            .map(|mask| {
198                ObjectSet::default().with_objects(
199                    objects
200                        .iter()
201                        .map(|o| service.render_object_to_proto(o, &mask))
202                        .collect(),
203                )
204            });
205        message
206    };
207
208    Ok(ExecuteTransactionResponse::default().with_transaction(executed_transaction))
209}