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 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 =
55 crate::read_mask_defaults::EXECUTE_TRANSACTION;
56const MAX_NUMBER_OF_SIGNATURES: usize = 2;
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 if request.signatures.len() > MAX_NUMBER_OF_SIGNATURES {
78 return Err(FieldViolation::new("signatures")
79 .with_description(format!(
80 "{} provided signatures exceeds the maximum allowed of {}",
81 request.signatures.len(),
82 MAX_NUMBER_OF_SIGNATURES
83 ))
84 .with_reason(ErrorReason::FieldInvalid)
85 .into());
86 }
87
88 let signatures = request
89 .signatures
90 .iter()
91 .enumerate()
92 .map(|(i, signature)| {
93 sui_sdk_types::UserSignature::try_from(signature).map_err(|e| {
94 FieldViolation::new_at("signatures", i)
95 .with_description(format!("invalid signature: {e}"))
96 .with_reason(ErrorReason::FieldInvalid)
97 })
98 })
99 .collect::<Result<Vec<_>, _>>()?;
100
101 let signed_transaction = sui_sdk_types::SignedTransaction {
102 transaction: transaction.clone(),
103 signatures: signatures.clone(),
104 };
105
106 let read_mask = {
107 let read_mask = request
108 .read_mask
109 .unwrap_or_else(|| FieldMask::from_str(EXECUTE_TRANSACTION_READ_MASK_DEFAULT));
110 read_mask
111 .validate::<ExecutedTransaction>()
112 .map_err(|path| {
113 FieldViolation::new("read_mask")
114 .with_description(format!("invalid read_mask path: {path}"))
115 .with_reason(ErrorReason::FieldInvalid)
116 })?;
117 FieldMaskTree::from(read_mask)
118 };
119
120 let request = sui_types::transaction_driver_types::ExecuteTransactionRequestV3 {
121 transaction: signed_transaction.try_into()?,
122 include_events: read_mask.contains(ExecutedTransaction::EVENTS_FIELD.name),
123 include_input_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
124 || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
125 || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
126 include_output_objects: read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name)
127 || read_mask.contains(ExecutedTransaction::OBJECTS_FIELD.name)
128 || read_mask.contains(ExecutedTransaction::EFFECTS_FIELD.name),
129 include_auxiliary_data: false,
130 };
131
132 let sui_types::transaction_driver_types::ExecuteTransactionResponseV3 {
133 effects:
134 sui_types::transaction_driver_types::FinalizedEffects {
135 effects,
136 finality_info: _,
137 },
138 events,
139 input_objects,
140 output_objects,
141 auxiliary_data: _,
142 } = executor.execute_transaction(request, None).await?;
143
144 let executed_transaction = {
145 let objects = {
148 let mut objects = sui_types::full_checkpoint_content::ObjectSet::default();
149 for o in input_objects
150 .into_iter()
151 .chain(output_objects.into_iter())
152 .flatten()
153 {
154 objects.insert(o);
155 }
156 objects
157 };
158
159 let events = read_mask
160 .subtree(ExecutedTransaction::EVENTS_FIELD)
161 .and_then(|mask| {
162 events.map(|events| service.render_events_to_proto(&events, &mask, &objects))
163 });
164
165 let balance_changes = if read_mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD) {
166 derive_balance_changes_2(&effects, &objects)
167 .into_iter()
168 .map(Into::into)
169 .collect()
170 } else {
171 vec![]
172 };
173
174 let effects = read_mask
175 .subtree(ExecutedTransaction::EFFECTS_FIELD)
176 .map(|mask| service.render_effects_to_proto(&effects, &[], &objects, &mask));
177
178 let mut message = ExecutedTransaction::default();
179 message.digest = read_mask
180 .contains(ExecutedTransaction::DIGEST_FIELD)
181 .then(|| transaction.digest().to_string());
182 message.transaction = read_mask
183 .subtree(ExecutedTransaction::TRANSACTION_FIELD)
184 .map(|mask| Transaction::merge_from(transaction, &mask));
185 message.signatures = read_mask
186 .subtree(ExecutedTransaction::SIGNATURES_FIELD)
187 .map(|mask| {
188 signatures
189 .into_iter()
190 .map(|s| UserSignature::merge_from(s, &mask))
191 .collect()
192 })
193 .unwrap_or_default();
194 message.effects = effects;
195 message.events = events;
196 message.balance_changes = balance_changes;
197 message.objects = read_mask
198 .subtree(
199 ExecutedTransaction::path_builder()
200 .objects()
201 .objects()
202 .finish(),
203 )
204 .map(|mask| {
205 ObjectSet::default().with_objects(
206 objects
207 .iter()
208 .map(|o| service.render_object_to_proto(o, &mask, &objects))
209 .collect(),
210 )
211 });
212 message
213 };
214
215 Ok(ExecuteTransactionResponse::default().with_transaction(executed_transaction))
216}