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