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 = "effects";
55const 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 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}