1use darkfi_serial::deserialize_async;
20use tinyjson::JsonValue;
21use tracing::{error, warn};
22
23use darkfi::{
24 rpc::jsonrpc::{
25 ErrorCode::{InternalError, InvalidParams},
26 JsonError, JsonResponse, JsonResult,
27 },
28 tx::Transaction,
29 util::encoding::base64,
30};
31
32use super::DarkfiNode;
33use crate::{server_error, RpcError};
34
35impl DarkfiNode {
36 pub async fn tx_simulate(&self, id: u16, params: JsonValue) -> JsonResult {
44 let Some(params) = params.get::<Vec<JsonValue>>() else {
45 return JsonError::new(InvalidParams, None, id).into()
46 };
47 if params.len() != 1 || !params[0].is_string() {
48 return JsonError::new(InvalidParams, None, id).into()
49 }
50
51 let mut validator = self.validator.write().await;
52 if !validator.synced {
53 error!(target: "darkfid::rpc::tx_simulate", "Blockchain is not synced");
54 return server_error(RpcError::NotSynced, id, None)
55 }
56
57 let tx_enc = params[0].get::<String>().unwrap().trim();
59 let tx_bytes = match base64::decode(tx_enc) {
60 Some(v) => v,
61 None => {
62 error!(target: "darkfid::rpc::tx_simulate", "Failed decoding base64 transaction");
63 return server_error(RpcError::ParseError, id, None)
64 }
65 };
66
67 let tx: Transaction = match deserialize_async(&tx_bytes).await {
68 Ok(v) => v,
69 Err(e) => {
70 error!(target: "darkfid::rpc::tx_simulate", "Failed deserializing bytes into Transaction: {e}");
71 return server_error(RpcError::ParseError, id, None)
72 }
73 };
74
75 let result = validator.append_tx(&tx, false).await;
77
78 if let Err(e) = validator
80 .consensus
81 .purge_unreferenced_trees(&mut self.registry.state.read().await.new_trees())
82 .await
83 {
84 error!(target: "darkfid::rpc::tx_simulate", "Purging unreferenced contract trees from the database failed: {e}");
85 return JsonError::new(InternalError, None, id).into()
86 }
87
88 if let Err(e) = result {
90 error!(target: "darkfid::rpc::tx_simulate", "Failed to validate state transition: {e}");
91 return server_error(RpcError::TxSimulationFail, id, None)
92 };
93
94 JsonResponse::new(JsonValue::Boolean(true), id).into()
95 }
96
97 pub async fn tx_broadcast(&self, id: u16, params: JsonValue) -> JsonResult {
107 let Some(params) = params.get::<Vec<JsonValue>>() else {
108 return JsonError::new(InvalidParams, None, id).into()
109 };
110 if params.len() != 1 || !params[0].is_string() {
111 return JsonError::new(InvalidParams, None, id).into()
112 }
113
114 let mut validator = self.validator.write().await;
115 if !validator.synced {
116 error!(target: "darkfid::rpc::tx_broadcast", "Blockchain is not synced");
117 return server_error(RpcError::NotSynced, id, None)
118 }
119
120 let tx_enc = params[0].get::<String>().unwrap().trim();
122 let tx_bytes = match base64::decode(tx_enc) {
123 Some(v) => v,
124 None => {
125 error!(target: "darkfid::rpc::tx_broadcast", "Failed decoding base64 transaction");
126 return server_error(RpcError::ParseError, id, None)
127 }
128 };
129
130 let tx: Transaction = match deserialize_async(&tx_bytes).await {
131 Ok(v) => v,
132 Err(e) => {
133 error!(target: "darkfid::rpc::tx_broadcast", "Failed deserializing bytes into Transaction: {e}");
134 return server_error(RpcError::ParseError, id, None)
135 }
136 };
137
138 let result = validator.append_tx(&tx, true).await;
140
141 if let Err(e) = validator
143 .consensus
144 .purge_unreferenced_trees(&mut self.registry.state.read().await.new_trees())
145 .await
146 {
147 error!(target: "darkfid::rpc::tx_broadcast", "Purging unreferenced contract trees from the database failed: {e}");
148 return JsonError::new(InternalError, None, id).into()
149 }
150
151 if let Err(e) = result {
153 error!(target: "darkfid::rpc::tx_broadcast", "Failed to append transaction to mempool: {e}");
154 return server_error(RpcError::TxSimulationFail, id, None)
155 };
156
157 self.p2p_handler.p2p.broadcast(&tx).await;
158 if !self.p2p_handler.p2p.is_connected() {
159 warn!(target: "darkfid::rpc::tx_broadcast", "No connected channels to broadcast tx");
160 }
161
162 let tx_hash = tx.hash().to_string();
163 JsonResponse::new(JsonValue::String(tx_hash), id).into()
164 }
165
166 pub async fn tx_pending(&self, id: u16, params: JsonValue) -> JsonResult {
173 let Some(params) = params.get::<Vec<JsonValue>>() else {
174 return JsonError::new(InvalidParams, None, id).into()
175 };
176 if !params.is_empty() {
177 return JsonError::new(InvalidParams, None, id).into()
178 }
179
180 let validator = self.validator.read().await;
181 if !validator.synced {
182 error!(target: "darkfid::rpc::tx_pending", "Blockchain is not synced");
183 return server_error(RpcError::NotSynced, id, None)
184 }
185
186 let pending_txs = match validator.blockchain.get_pending_txs() {
187 Ok(v) => v,
188 Err(e) => {
189 error!(target: "darkfid::rpc::tx_pending", "Failed fetching pending txs: {e}");
190 return JsonError::new(InternalError, None, id).into()
191 }
192 };
193
194 let pending_txs: Vec<JsonValue> =
195 pending_txs.iter().map(|x| JsonValue::String(x.hash().to_string())).collect();
196
197 JsonResponse::new(JsonValue::Array(pending_txs), id).into()
198 }
199
200 pub async fn tx_rebroadcast_pending(&self, id: u16, params: JsonValue) -> JsonResult {
209 let Some(params) = params.get::<Vec<JsonValue>>() else {
210 return JsonError::new(InvalidParams, None, id).into()
211 };
212 if !params.is_empty() {
213 return JsonError::new(InvalidParams, None, id).into()
214 }
215
216 let validator = self.validator.read().await;
217 if !validator.synced {
218 error!(target: "darkfid::rpc::tx_rebroadcast_pending", "Blockchain is not synced");
219 return server_error(RpcError::NotSynced, id, None)
220 }
221
222 let pending = validator.blockchain.transactions.pending.iter();
225 drop(validator);
226
227 for value in pending.values() {
229 let value = match value {
230 Ok(v) => v,
231 Err(e) => {
232 error!(target: "darkfid::rpc::tx_rebroadcast_pending", "Failed retrieving pending tx: {e}");
233 return JsonError::new(InternalError, None, id).into()
234 }
235 };
236 let tx = match deserialize_async::<Transaction>(&value).await {
237 Ok(tx) => tx,
238 Err(e) => {
239 error!(target: "darkfid::rpc::tx_rebroadcast_pending", "Failed deserialized pending tx: {e}");
240 return JsonError::new(InternalError, None, id).into()
241 }
242 };
243 self.p2p_handler.p2p.broadcast(&tx).await;
244 }
245
246 JsonResponse::new(JsonValue::Boolean(true), id).into()
247 }
248
249 pub async fn tx_clean_pending(&self, id: u16, params: JsonValue) -> JsonResult {
258 let Some(params) = params.get::<Vec<JsonValue>>() else {
259 return JsonError::new(InvalidParams, None, id).into()
260 };
261 if !params.is_empty() {
262 return JsonError::new(InvalidParams, None, id).into()
263 }
264
265 let validator = self.validator.write().await;
266 if !validator.synced {
267 error!(target: "darkfid::rpc::tx_clean_pending", "Blockchain is not synced");
268 return server_error(RpcError::NotSynced, id, None)
269 }
270
271 if let Err(e) = validator.blockchain.transactions.pending.clear() {
273 error!(target: "darkfid::rpc::tx_clean_pending", "Failed cleaning pending txs: {e}");
274 return JsonError::new(InternalError, None, id).into()
275 };
276
277 JsonResponse::new(JsonValue::Boolean(true), id).into()
278 }
279
280 pub async fn tx_calculate_fee(&self, id: u16, params: JsonValue) -> JsonResult {
288 let Some(params) = params.get::<Vec<JsonValue>>() else {
289 return JsonError::new(InvalidParams, None, id).into()
290 };
291 if params.len() != 2 || !params[0].is_string() || !params[1].is_bool() {
292 return JsonError::new(InvalidParams, None, id).into()
293 }
294
295 let validator = self.validator.read().await;
296 if !validator.synced {
297 error!(target: "darkfid::rpc::tx_calculate_fee", "Blockchain is not synced");
298 return server_error(RpcError::NotSynced, id, None)
299 }
300
301 let tx_enc = params[0].get::<String>().unwrap().trim();
303 let tx_bytes = match base64::decode(tx_enc) {
304 Some(v) => v,
305 None => {
306 error!(target: "darkfid::rpc::tx_calculate_fee", "Failed decoding base64 transaction");
307 return server_error(RpcError::ParseError, id, None)
308 }
309 };
310
311 let tx: Transaction = match deserialize_async(&tx_bytes).await {
312 Ok(v) => v,
313 Err(e) => {
314 error!(target: "darkfid::rpc::tx_calculate_fee", "Failed deserializing bytes into Transaction: {e}");
315 return server_error(RpcError::ParseError, id, None)
316 }
317 };
318
319 let include_fee = params[1].get::<bool>().unwrap();
321
322 let result = validator.calculate_fee(&tx, *include_fee).await;
324
325 if let Err(e) = validator
327 .consensus
328 .purge_unreferenced_trees(&mut self.registry.state.read().await.new_trees())
329 .await
330 {
331 error!(target: "darkfid::rpc::tx_calculate_fee", "Purging unreferenced contract trees from the database failed: {e}");
332 return JsonError::new(InternalError, None, id).into()
333 }
334
335 if let Err(e) = result {
337 error!(target: "darkfid::rpc::tx_calculate_fee", "Failed to validate state transition: {e}");
338 return server_error(RpcError::TxGasCalculationFail, id, None)
339 };
340
341 JsonResponse::new(JsonValue::Number(result.unwrap() as f64), id).into()
342 }
343}