rpc_tx.rs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi_serial::deserialize;
  19. use log::error;
  20. use serde_json::{json, Value};
  21. use darkfi::{
  22. rpc::jsonrpc::{
  23. ErrorCode::{InternalError, InvalidParams},
  24. JsonError, JsonResponse, JsonResult,
  25. },
  26. tx::Transaction,
  27. };
  28. use super::Darkfid;
  29. use crate::{server_error, RpcError};
  30. impl Darkfid {
  31. // RPCAPI:
  32. // Simulate a network state transition with the given transaction.
  33. // Returns `true` if the transaction is valid, otherwise, a corresponding
  34. // error.
  35. //
  36. // --> {"jsonrpc": "2.0", "method": "tx.simulate", "params": ["base58encodedTX"], "id": 1}
  37. // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
  38. pub async fn tx_simulate(&self, id: Value, params: &[Value]) -> JsonResult {
  39. if params.len() != 1 || !params[0].is_string() {
  40. return JsonError::new(InvalidParams, None, id).into()
  41. }
  42. if !self.validator.read().await.synced {
  43. error!(target: "darkfid::rpc::tx_simulate", "Blockchain is not synced");
  44. return server_error(RpcError::NotSynced, id, None)
  45. }
  46. // Try to deserialize the transaction
  47. let tx_bytes = match bs58::decode(params[0].as_str().unwrap().trim()).into_vec() {
  48. Ok(v) => v,
  49. Err(e) => {
  50. error!(target: "darkfid::rpc::tx_simulate", "Failed decoding base58 transaction: {}", e);
  51. return server_error(RpcError::ParseError, id, None)
  52. }
  53. };
  54. let tx: Transaction = match deserialize(&tx_bytes) {
  55. Ok(v) => v,
  56. Err(e) => {
  57. error!(target: "darkfid::rpc::tx_simulate", "Failed deserializing bytes into Transaction: {}", e);
  58. return server_error(RpcError::ParseError, id, None)
  59. }
  60. };
  61. // Simulate state transition
  62. let lock = self.validator.read().await;
  63. let current_slot = lock.consensus.time_keeper.current_slot();
  64. let result = lock.add_transactions(&[tx], current_slot, false).await;
  65. if result.is_err() {
  66. error!(
  67. target: "darkfid::rpc::tx_simulate", "Failed to validate state transition: {}",
  68. result.err().unwrap()
  69. );
  70. return server_error(RpcError::TxSimulationFail, id, None)
  71. };
  72. JsonResponse::new(json!(true), id).into()
  73. }
  74. // RPCAPI:
  75. // Broadcast a given transaction to the P2P network.
  76. // The function will first simulate the state transition in order to see
  77. // if the transaction is actually valid, and in turn it will return an
  78. // error if this is the case. Otherwise, a transaction ID will be returned.
  79. //
  80. // --> {"jsonrpc": "2.0", "method": "tx.broadcast", "params": ["base58encodedTX"], "id": 1}
  81. // <-- {"jsonrpc": "2.0", "result": "txID...", "id": 1}
  82. pub async fn tx_broadcast(&self, id: Value, params: &[Value]) -> JsonResult {
  83. if params.len() != 1 || !params[0].is_string() {
  84. return JsonError::new(InvalidParams, None, id).into()
  85. }
  86. if !self.validator.read().await.synced {
  87. error!(target: "darkfid::rpc::tx_broadcast", "Blockchain is not synced");
  88. return server_error(RpcError::NotSynced, id, None)
  89. }
  90. // Try to deserialize the transaction
  91. let tx_bytes = match bs58::decode(params[0].as_str().unwrap().trim()).into_vec() {
  92. Ok(v) => v,
  93. Err(e) => {
  94. error!(target: "darkfid::rpc::tx_broadcast", "Failed decoding base58 transaction: {}", e);
  95. return server_error(RpcError::ParseError, id, None)
  96. }
  97. };
  98. let tx: Transaction = match deserialize(&tx_bytes) {
  99. Ok(v) => v,
  100. Err(e) => {
  101. error!(target: "darkfid::rpc::tx_broadcast", "Failed deserializing bytes into Transaction: {}", e);
  102. return server_error(RpcError::ParseError, id, None)
  103. }
  104. };
  105. if self.consensus_p2p.is_some() {
  106. // Consensus participants can directly perform
  107. // the state transition check and append to their
  108. // pending transactions store.
  109. if self.validator.write().await.append_tx(tx.clone()).await.is_err() {
  110. error!(target: "darkfid::rpc::tx_broadcast", "Failed to append transaction to mempool");
  111. return server_error(RpcError::TxSimulationFail, id, None)
  112. }
  113. } else {
  114. // We'll perform the state transition check here.
  115. let lock = self.validator.read().await;
  116. let current_slot = lock.consensus.time_keeper.current_slot();
  117. let result = lock.add_transactions(&[tx.clone()], current_slot, false).await;
  118. if result.is_err() {
  119. error!(
  120. target: "darkfid::rpc::tx_broadcast", "Failed to validate state transition: {}",
  121. result.err().unwrap()
  122. );
  123. return server_error(RpcError::TxSimulationFail, id, None)
  124. };
  125. }
  126. self.sync_p2p.broadcast(&tx).await;
  127. if self.sync_p2p.channels().lock().await.is_empty() {
  128. error!(target: "darkfid::rpc::tx_broadcast", "Failed broadcasting tx, no connected channels");
  129. return server_error(RpcError::TxBroadcastFail, id, None)
  130. }
  131. let tx_hash = tx.hash().to_string();
  132. JsonResponse::new(json!(tx_hash), id).into()
  133. }
  134. // RPCAPI:
  135. // Queries the node pending transactions store to retrieve all transactions.
  136. // Returns a vector of serialized `Transaction` objects.
  137. //
  138. // --> {"jsonrpc": "2.0", "method": "tx.pending", "params": [], "id": 1}
  139. // <-- {"jsonrpc": "2.0", "result": "[TxHash,...]", "id": 1}
  140. pub async fn tx_pending(&self, id: Value, params: &[Value]) -> JsonResult {
  141. if !params.is_empty() {
  142. return JsonError::new(InvalidParams, None, id).into()
  143. }
  144. if !self.validator.read().await.synced {
  145. error!(target: "darkfid::rpc::tx_pending", "Blockchain is not synced");
  146. return server_error(RpcError::NotSynced, id, None)
  147. }
  148. let pending_txs = match self.validator.read().await.blockchain.get_pending_txs() {
  149. Ok(v) => v,
  150. Err(e) => {
  151. error!(target: "darkfid::rpc::tx_pending", "Failed fetching pending txs: {}", e);
  152. return JsonError::new(InternalError, None, id).into()
  153. }
  154. };
  155. let pending_txs: Vec<String> = pending_txs.iter().map(|x| x.hash().to_string()).collect();
  156. JsonResponse::new(json!(pending_txs), id).into()
  157. }
  158. // RPCAPI:
  159. // Queries the node pending transactions store to remove all transactions.
  160. // Returns a vector of serialized `Transaction` objects.
  161. //
  162. // --> {"jsonrpc": "2.0", "method": "tx.clean_pending", "params": [], "id": 1}
  163. // <-- {"jsonrpc": "2.0", "result": "[TxHash,...]", "id": 1}
  164. pub async fn tx_clean_pending(&self, id: Value, params: &[Value]) -> JsonResult {
  165. if !params.is_empty() {
  166. return JsonError::new(InvalidParams, None, id).into()
  167. }
  168. if !self.validator.read().await.synced {
  169. error!(target: "darkfid::rpc::tx_clean_pending", "Blockchain is not synced");
  170. return server_error(RpcError::NotSynced, id, None)
  171. }
  172. let pending_txs = match self.validator.read().await.blockchain.get_pending_txs() {
  173. Ok(v) => v,
  174. Err(e) => {
  175. error!(target: "darkfid::rpc::tx_clean_pending", "Failed fetching pending txs: {}", e);
  176. return JsonError::new(InternalError, None, id).into()
  177. }
  178. };
  179. if let Err(e) = self.validator.read().await.blockchain.remove_pending_txs(&pending_txs) {
  180. error!(target: "darkfid::rpc::tx_clean_pending", "Failed fetching pending txs: {}", e);
  181. return JsonError::new(InternalError, None, id).into()
  182. };
  183. let pending_txs: Vec<String> = pending_txs.iter().map(|x| x.hash().to_string()).collect();
  184. JsonResponse::new(json!(pending_txs), id).into()
  185. }
  186. }