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