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 if let Err(e) = validator.append_tx(&tx, false).await {
77 error!(target: "darkfid::rpc::tx_simulate", "Failed to validate state transition: {e}");
78 return server_error(RpcError::TxSimulationFail, id, None)
79 };
80
81 JsonResponse::new(JsonValue::Boolean(true), id).into()
82 }
83
84 pub async fn tx_broadcast(&self, id: u16, params: JsonValue) -> JsonResult {
94 let Some(params) = params.get::<Vec<JsonValue>>() else {
95 return JsonError::new(InvalidParams, None, id).into()
96 };
97 if params.len() != 1 || !params[0].is_string() {
98 return JsonError::new(InvalidParams, None, id).into()
99 }
100
101 let mut validator = self.validator.write().await;
102 if !validator.synced {
103 error!(target: "darkfid::rpc::tx_broadcast", "Blockchain is not synced");
104 return server_error(RpcError::NotSynced, id, None)
105 }
106
107 let tx_enc = params[0].get::<String>().unwrap().trim();
109 let tx_bytes = match base64::decode(tx_enc) {
110 Some(v) => v,
111 None => {
112 error!(target: "darkfid::rpc::tx_broadcast", "Failed decoding base64 transaction");
113 return server_error(RpcError::ParseError, id, None)
114 }
115 };
116
117 let tx: Transaction = match deserialize_async(&tx_bytes).await {
118 Ok(v) => v,
119 Err(e) => {
120 error!(target: "darkfid::rpc::tx_broadcast", "Failed deserializing bytes into Transaction: {e}");
121 return server_error(RpcError::ParseError, id, None)
122 }
123 };
124
125 if let Err(e) = validator.append_tx(&tx, true).await {
127 error!(target: "darkfid::rpc::tx_broadcast", "Failed to append transaction to mempool: {e}");
128 return server_error(RpcError::TxSimulationFail, id, None)
129 };
130
131 self.p2p_handler.p2p.broadcast(&tx).await;
132 if !self.p2p_handler.p2p.is_connected() {
133 warn!(target: "darkfid::rpc::tx_broadcast", "No connected channels to broadcast tx");
134 }
135
136 let tx_hash = tx.hash().to_string();
137 JsonResponse::new(JsonValue::String(tx_hash), id).into()
138 }
139
140 pub async fn tx_pending(&self, id: u16, params: JsonValue) -> JsonResult {
147 let Some(params) = params.get::<Vec<JsonValue>>() else {
148 return JsonError::new(InvalidParams, None, id).into()
149 };
150 if !params.is_empty() {
151 return JsonError::new(InvalidParams, None, id).into()
152 }
153
154 let validator = self.validator.read().await;
155 if !validator.synced {
156 error!(target: "darkfid::rpc::tx_pending", "Blockchain is not synced");
157 return server_error(RpcError::NotSynced, id, None)
158 }
159
160 let pending_txs = match validator.blockchain.get_pending_txs() {
161 Ok(v) => v,
162 Err(e) => {
163 error!(target: "darkfid::rpc::tx_pending", "Failed fetching pending txs: {e}");
164 return JsonError::new(InternalError, None, id).into()
165 }
166 };
167
168 let pending_txs: Vec<JsonValue> =
169 pending_txs.iter().map(|x| JsonValue::String(x.hash().to_string())).collect();
170
171 JsonResponse::new(JsonValue::Array(pending_txs), id).into()
172 }
173
174 pub async fn tx_clean_pending(&self, id: u16, params: JsonValue) -> JsonResult {
183 let Some(params) = params.get::<Vec<JsonValue>>() else {
184 return JsonError::new(InvalidParams, None, id).into()
185 };
186 if !params.is_empty() {
187 return JsonError::new(InvalidParams, None, id).into()
188 }
189
190 let mut validator = self.validator.write().await;
191 if !validator.synced {
192 error!(target: "darkfid::rpc::tx_clean_pending", "Blockchain is not synced");
193 return server_error(RpcError::NotSynced, id, None)
194 }
195
196 let registry_txs = self.registry.state.read().await.proposed_transactions();
198
199 if let Err(e) = validator.consensus.purge_unproposed_pending_txs(registry_txs).await {
201 error!(target: "darkfid::rpc::tx_clean_pending", "Failed removing pending txs: {e}");
202 return JsonError::new(InternalError, None, id).into()
203 };
204
205 JsonResponse::new(JsonValue::Boolean(true), id).into()
206 }
207
208 pub async fn tx_calculate_fee(&self, id: u16, params: JsonValue) -> JsonResult {
216 let Some(params) = params.get::<Vec<JsonValue>>() else {
217 return JsonError::new(InvalidParams, None, id).into()
218 };
219 if params.len() != 2 || !params[0].is_string() || !params[1].is_bool() {
220 return JsonError::new(InvalidParams, None, id).into()
221 }
222
223 let validator = self.validator.read().await;
224 if !validator.synced {
225 error!(target: "darkfid::rpc::tx_calculate_fee", "Blockchain is not synced");
226 return server_error(RpcError::NotSynced, id, None)
227 }
228
229 let tx_enc = params[0].get::<String>().unwrap().trim();
231 let tx_bytes = match base64::decode(tx_enc) {
232 Some(v) => v,
233 None => {
234 error!(target: "darkfid::rpc::tx_calculate_fee", "Failed decoding base64 transaction");
235 return server_error(RpcError::ParseError, id, None)
236 }
237 };
238
239 let tx: Transaction = match deserialize_async(&tx_bytes).await {
240 Ok(v) => v,
241 Err(e) => {
242 error!(target: "darkfid::rpc::tx_calculate_fee", "Failed deserializing bytes into Transaction: {e}");
243 return server_error(RpcError::ParseError, id, None)
244 }
245 };
246
247 let include_fee = params[1].get::<bool>().unwrap();
249
250 let result = validator.calculate_fee(&tx, *include_fee).await;
252 if result.is_err() {
253 error!(
254 target: "darkfid::rpc::tx_calculate_fee", "Failed to validate state transition: {}",
255 result.err().unwrap()
256 );
257 return server_error(RpcError::TxGasCalculationFail, id, None)
258 };
259
260 JsonResponse::new(JsonValue::Number(result.unwrap() as f64), id).into()
261 }
262}