sui_rpc_api/grpc/v2/transaction_execution_service/
mod.rs1use 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;
70const 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 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}