rpc_tx.rs 8.4 KB

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