1use std::sync::Arc;
6
7use async_trait::async_trait;
8use fastcrypto::encoding::Base64;
9use iota_json::IotaJsonValue;
10use iota_json_rpc::IotaRpcModule;
11use iota_json_rpc_api::{WriteApiClient, WriteApiServer, error_object_from_rpc};
12use iota_json_rpc_types::{
13 DevInspectArgs, DevInspectResults, DryRunTransactionBlockResponse, IotaMoveViewCallResults,
14 IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, IotaTypeTag,
15 MoveFunctionName,
16};
17use iota_open_rpc::Module;
18use iota_protocol_config::Chain;
19use iota_transaction_builder::TransactionBuilder;
20use iota_types::{
21 base_types::IotaAddress, iota_serde::BigInt, quorum_driver_types::ExecuteTransactionRequestType,
22};
23use jsonrpsee::{RpcModule, core::RpcResult, http_client::HttpClient};
24
25use crate::{
26 apis::error::Error as ApiError, errors::IndexerError,
27 optimistic_indexing::OptimisticTransactionExecutor, read::IndexerReader,
28 store::package_resolver::IndexerStorePackageResolver,
29 types::IotaTransactionBlockResponseWithOptions,
30};
31
32#[derive(Clone)]
33pub struct WriteApi {
34 fullnode: HttpClient,
35 transaction_builder: TransactionBuilder,
36 package_resolver: IndexerStorePackageResolver,
37}
38
39#[derive(Clone)]
40pub struct OptimisticWriteApi {
41 write_api: WriteApi,
42 optimistic_tx_executor: OptimisticTransactionExecutor,
43}
44
45impl WriteApi {
46 pub fn new(fullnode_client: HttpClient, reader: IndexerReader) -> Self {
47 let package_resolver = IndexerStorePackageResolver::new(reader.get_pool());
48 let data_reader = Arc::new(reader);
49 Self {
50 fullnode: fullnode_client,
51 transaction_builder: TransactionBuilder::new(data_reader),
52 package_resolver,
53 }
54 }
55}
56
57impl OptimisticWriteApi {
58 pub fn new(write_api: WriteApi, optimistic_tx_executor: OptimisticTransactionExecutor) -> Self {
59 Self {
60 write_api,
61 optimistic_tx_executor,
62 }
63 }
64
65 pub fn fullnode_client(&self) -> &HttpClient {
66 &self.write_api.fullnode
67 }
68}
69
70#[async_trait]
71impl WriteApiServer for WriteApi {
72 async fn execute_transaction_block(
73 &self,
74 tx_bytes: Base64,
75 signatures: Vec<Base64>,
76 options: Option<IotaTransactionBlockResponseOptions>,
77 request_type: Option<ExecuteTransactionRequestType>,
78 ) -> RpcResult<IotaTransactionBlockResponse> {
79 let iota_transaction_response = self
80 .fullnode
81 .execute_transaction_block(tx_bytes, signatures, options.clone(), request_type)
82 .await
83 .map_err(error_object_from_rpc)?;
84 Ok(IotaTransactionBlockResponseWithOptions {
85 response: iota_transaction_response,
86 options: options.unwrap_or_default(),
87 }
88 .into())
89 }
90
91 async fn dev_inspect_transaction_block(
92 &self,
93 sender_address: IotaAddress,
94 tx_bytes: Base64,
95 gas_price: Option<BigInt<u64>>,
96 epoch: Option<BigInt<u64>>,
97 additional_args: Option<DevInspectArgs>,
98 ) -> RpcResult<DevInspectResults> {
99 self.fullnode
100 .dev_inspect_transaction_block(
101 sender_address,
102 tx_bytes,
103 gas_price,
104 epoch,
105 additional_args,
106 )
107 .await
108 .map_err(error_object_from_rpc)
109 }
110
111 async fn dry_run_transaction_block(
112 &self,
113 tx_bytes: Base64,
114 ) -> RpcResult<DryRunTransactionBlockResponse> {
115 self.fullnode
116 .dry_run_transaction_block(tx_bytes)
117 .await
118 .map_err(error_object_from_rpc)
119 }
120
121 async fn view_function_call(
122 &self,
123 function_name: String,
124 type_args: Option<Vec<IotaTypeTag>>,
125 call_args: Vec<IotaJsonValue>,
126 ) -> RpcResult<IotaMoveViewCallResults> {
127 let MoveFunctionName {
128 package,
129 module,
130 function,
131 } = function_name.as_str().parse().map_err(IndexerError::from)?;
132 let sender = IotaAddress::ZERO;
133 let tx_kind = self
134 .transaction_builder
135 .move_view_call_tx_kind(
136 package,
137 &module,
138 &function,
139 type_args.unwrap_or_default(),
140 call_args,
141 )
142 .await
143 .map_err(IndexerError::from)?;
144 let tx_bytes = Base64::from_bytes(&bcs::to_bytes(&tx_kind).map_err(IndexerError::from)?);
145 let dev_inspect_results = self
146 .dev_inspect_transaction_block(sender, tx_bytes, None, None, None)
147 .await?;
148 Ok(IotaMoveViewCallResults::from_dev_inspect_results(
149 self.package_resolver.clone(),
150 dev_inspect_results,
151 )
152 .await
153 .map_err(IndexerError::from)?)
154 }
155}
156
157#[async_trait]
158impl WriteApiServer for OptimisticWriteApi {
159 async fn execute_transaction_block(
160 &self,
161 tx_bytes: Base64,
162 signatures: Vec<Base64>,
163 options: Option<IotaTransactionBlockResponseOptions>,
164 _request_type: Option<ExecuteTransactionRequestType>,
165 ) -> RpcResult<IotaTransactionBlockResponse> {
166 let iota_transaction_response = self
167 .optimistic_tx_executor
168 .execute_and_index_transaction(tx_bytes, signatures, options.clone())
169 .await?;
170 Ok(IotaTransactionBlockResponseWithOptions {
171 response: iota_transaction_response,
172 options: options.unwrap_or_default(),
173 }
174 .into())
175 }
176
177 async fn dev_inspect_transaction_block(
178 &self,
179 sender_address: IotaAddress,
180 tx_bytes: Base64,
181 gas_price: Option<BigInt<u64>>,
182 epoch: Option<BigInt<u64>>,
183 additional_args: Option<DevInspectArgs>,
184 ) -> RpcResult<DevInspectResults> {
185 self.write_api
186 .dev_inspect_transaction_block(
187 sender_address,
188 tx_bytes,
189 gas_price,
190 epoch,
191 additional_args,
192 )
193 .await
194 }
195
196 async fn dry_run_transaction_block(
197 &self,
198 tx_bytes: Base64,
199 ) -> RpcResult<DryRunTransactionBlockResponse> {
200 self.write_api.dry_run_transaction_block(tx_bytes).await
201 }
202
203 async fn view_function_call(
204 &self,
205 function_name: String,
206 type_args: Option<Vec<IotaTypeTag>>,
207 call_args: Vec<IotaJsonValue>,
208 ) -> RpcResult<IotaMoveViewCallResults> {
209 let chain = self
210 .optimistic_tx_executor
211 .read
212 .get_chain_identifier_in_blocking_task()
213 .await?
214 .chain();
215 if !matches!(chain, Chain::Unknown) {
216 return Err(ApiError::UnsupportedFeature(format!(
217 "View calls are not yet supported on {}",
218 chain.as_str()
219 ))
220 .into());
221 }
222
223 self.write_api
224 .view_function_call(function_name, type_args, call_args)
225 .await
226 }
227}
228
229impl IotaRpcModule for WriteApi {
230 fn rpc(self) -> RpcModule<Self> {
231 self.into_rpc()
232 }
233
234 fn rpc_doc_module() -> Module {
235 iota_json_rpc_api::WriteApiOpenRpc::module_doc()
236 }
237}
238
239impl IotaRpcModule for OptimisticWriteApi {
240 fn rpc(self) -> RpcModule<Self> {
241 self.into_rpc()
242 }
243
244 fn rpc_doc_module() -> Module {
245 iota_json_rpc_api::WriteApiOpenRpc::module_doc()
246 }
247}