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::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 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}