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 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;
60const 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 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}