rpc_tx.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. use std::str::FromStr;
  2. use darkfi_serial::{deserialize, serialize};
  3. use log::{error, warn};
  4. use serde_json::{json, Value};
  5. use darkfi::{
  6. crypto::{address::Address, keypair::PublicKey, token_id},
  7. rpc::jsonrpc::{ErrorCode::InvalidParams, JsonError, JsonResponse, JsonResult},
  8. tx::Transaction,
  9. };
  10. use super::Darkfid;
  11. use crate::{server_error, RpcError};
  12. impl Darkfid {
  13. // RPCAPI:
  14. // Transfer a given amount of some token to the given address.
  15. // Returns a transaction ID upon success.
  16. //
  17. // * `dest_addr` -> Recipient's DarkFi address
  18. // * `token_id` -> ID of the token to send
  19. // * `12345` -> Amount in `u64` of the funds to send
  20. //
  21. // --> {"jsonrpc": "2.0", "method": "tx.transfer", "params": ["dest_addr", "token_id", 12345], "id": 1}
  22. // <-- {"jsonrpc": "2.0", "result": "txID...", "id": 1}
  23. pub async fn tx_transfer(&self, id: Value, params: &[Value]) -> JsonResult {
  24. if params.len() != 3 ||
  25. !params[0].is_string() ||
  26. !params[1].is_string() ||
  27. !params[2].is_u64()
  28. {
  29. return JsonError::new(InvalidParams, None, id).into()
  30. }
  31. if !(*self.synced.lock().await) {
  32. error!("[RPC] tx.transfer: Blockchain is not synced");
  33. return server_error(RpcError::NotSynced, id, None)
  34. }
  35. let address = params[0].as_str().unwrap();
  36. let token = params[1].as_str().unwrap();
  37. let amount = params[2].as_u64().unwrap();
  38. let address = match Address::from_str(address) {
  39. Ok(v) => v,
  40. Err(e) => {
  41. error!("[RPC] tx.transfer: Failed parsing address from string: {}", e);
  42. return server_error(RpcError::InvalidAddressParam, id, None)
  43. }
  44. };
  45. let pubkey = match PublicKey::try_from(address) {
  46. Ok(v) => v,
  47. Err(e) => {
  48. error!("[RPC] tx.transfer: Failed parsing PublicKey from Address: {}", e);
  49. return server_error(RpcError::ParseError, id, None)
  50. }
  51. };
  52. let token_id = match token_id::parse_b58(token) {
  53. Ok(v) => v,
  54. Err(e) => {
  55. error!("[RPC] tx.transfer: Failed parsing Token ID from string: {}", e);
  56. return server_error(RpcError::ParseError, id, None)
  57. }
  58. };
  59. let tx = match self
  60. .client
  61. .build_transaction(
  62. pubkey,
  63. amount,
  64. token_id,
  65. false,
  66. self.validator_state.read().await.state_machine.clone(),
  67. )
  68. .await
  69. {
  70. Ok(v) => v,
  71. Err(e) => {
  72. error!("tx.transfer: Failed building transaction: {}", e);
  73. return server_error(RpcError::TxBuildFail, id, None)
  74. }
  75. };
  76. if let Some(sync_p2p) = &self.sync_p2p {
  77. if let Err(e) = sync_p2p.broadcast(tx.clone()).await {
  78. error!("[RPC] tx.transfer: Failed broadcasting transaction: {}", e);
  79. return server_error(RpcError::TxBroadcastFail, id, None)
  80. }
  81. } else {
  82. warn!("[RPC] tx.transfer: No sync P2P network, not broadcasting transaction.");
  83. return server_error(RpcError::TxBroadcastFail, id, None)
  84. }
  85. let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string();
  86. JsonResponse::new(json!(tx_hash), id).into()
  87. }
  88. // RPCAPI:
  89. // Simulate a network state transition with the given transaction.
  90. // Returns `true` if the transaction is valid, otherwise, a corresponding
  91. // error.
  92. //
  93. // --> {"jsonrpc": "2.0", "method": "tx.simulate", "params": ["base58encodedTX"], "id": 1}
  94. // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
  95. pub async fn tx_simulate(&self, id: Value, params: &[Value]) -> JsonResult {
  96. if params.len() != 1 || !params[0].is_string() {
  97. return JsonError::new(InvalidParams, None, id).into()
  98. }
  99. if !(*self.synced.lock().await) {
  100. error!("[RPC] tx.simulate: Blockchain is not synced");
  101. return server_error(RpcError::NotSynced, id, None)
  102. }
  103. // Try to deserialize the transaction
  104. let tx_bytes = match bs58::decode(params[0].as_str().unwrap().trim()).into_vec() {
  105. Ok(v) => v,
  106. Err(e) => {
  107. error!("[RPC] tx.simulate: Failed decoding base58 transaction: {}", e);
  108. return server_error(RpcError::ParseError, id, None)
  109. }
  110. };
  111. let tx: Transaction = match deserialize(&tx_bytes) {
  112. Ok(v) => v,
  113. Err(e) => {
  114. error!("[RPC] tx.simulate: Failed deserializing bytes into Transaction: {}", e);
  115. return server_error(RpcError::ParseError, id, None)
  116. }
  117. };
  118. // Simulate state transition
  119. if let Err(e) = self.simulate_transaction(&tx).await {
  120. error!("[RPC] tx.broadcast: Failed to validate state transition: {}", e);
  121. return server_error(RpcError::TxSimulationFail, id, None)
  122. }
  123. JsonResponse::new(json!(true), id).into()
  124. }
  125. // RPCAPI:
  126. // Broadcast a given transaction to the P2P network.
  127. // The function will first simulate the state transition in order to see
  128. // if the transaction is actually valid, and in turn it will return an
  129. // error if this is the case. Otherwise, a transaction ID will be returned.
  130. //
  131. // --> {"jsonrpc": "2.0", "method": "tx.broadcast", "params": ["base58encodedTX"], "id": 1}
  132. // <-- {"jsonrpc": "2.0", "result": "txID...", "id": 1}
  133. pub async fn tx_broadcast(&self, id: Value, params: &[Value]) -> JsonResult {
  134. if params.len() != 1 || !params[0].is_string() {
  135. return JsonError::new(InvalidParams, None, id).into()
  136. }
  137. if !(*self.synced.lock().await) {
  138. error!("[RPC] tx.transfer: Blockchain is not synced");
  139. return server_error(RpcError::NotSynced, id, None)
  140. }
  141. // Try to deserialize the transaction
  142. let tx_bytes = match bs58::decode(params[0].as_str().unwrap().trim()).into_vec() {
  143. Ok(v) => v,
  144. Err(e) => {
  145. error!("[RPC] tx.broadcast: Failed decoding base58 transaction: {}", e);
  146. return server_error(RpcError::ParseError, id, None)
  147. }
  148. };
  149. let tx: Transaction = match deserialize(&tx_bytes) {
  150. Ok(v) => v,
  151. Err(e) => {
  152. error!("[RPC] tx.broadcast: Failed deserializing bytes into Transaction: {}", e);
  153. return server_error(RpcError::ParseError, id, None)
  154. }
  155. };
  156. // Simulate state transition
  157. if let Err(e) = self.simulate_transaction(&tx).await {
  158. error!("[RPC] tx.broadcast: Failed to validate state transition: {}", e);
  159. return server_error(RpcError::TxSimulationFail, id, None)
  160. }
  161. // TODO: Should we apply the state transition locally before broadcasting it?
  162. if let Some(sync_p2p) = &self.sync_p2p {
  163. if let Err(e) = sync_p2p.broadcast(tx.clone()).await {
  164. error!("[RPC] tx.broadcast: Failed broadcasting transaction: {}", e);
  165. return server_error(RpcError::TxBroadcastFail, id, None)
  166. }
  167. // TODO: Mark coin as spent in the wallet
  168. } else {
  169. warn!("[RPC] tx.broadcast: No sync P2P network, not broadcasting transaction.");
  170. return server_error(RpcError::TxBroadcastFail, id, None)
  171. }
  172. let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string();
  173. JsonResponse::new(json!(tx_hash), id).into()
  174. }
  175. }