iota_indexer/apis/
write_api.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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}