drk.rs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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::{
  19. system::{sleep, Publisher, PublisherPtr, StoppableTask},
  20. tx::Transaction,
  21. Result as DarkFiResult,
  22. };
  23. use darkfi_money_contract::model::TokenId;
  24. use darkfi_sdk::crypto::keypair::{Address, Network, PublicKey, StandardAddress};
  25. use darkfi_serial::{serialize, Decodable, Encodable};
  26. use drk::{rpc::subscribe_blocks, Drk};
  27. use parking_lot::Mutex as SyncMutex;
  28. use smol::{channel::unbounded, lock::RwLock};
  29. use std::{
  30. io::Cursor,
  31. sync::{Arc, OnceLock, Weak},
  32. };
  33. use url::Url;
  34. use crate::{
  35. error::{Error, Result},
  36. prop::BatchGuardPtr,
  37. scene::{MethodCallSub, Pimpl, SceneNode, SceneNodePtr, SceneNodeType, SceneNodeWeak},
  38. ExecutorPtr,
  39. };
  40. // TODO: should be configurable at runtime
  41. //const DARKFID_ENDPOINT: &str = "tcp://127.0.0.1:18345";
  42. const DARKFID_ENDPOINT: &str = "tcp+tls://node0.testnet.dark.fi:18345";
  43. const DARKFID_RETRY_TIME: u64 = 20;
  44. #[cfg(target_os = "android")]
  45. mod paths {
  46. use crate::android::{get_appdata_path, get_external_storage_path};
  47. use std::path::PathBuf;
  48. pub fn get_cache_path() -> PathBuf {
  49. get_external_storage_path().join("drk/cache")
  50. }
  51. pub fn get_wallet_path() -> PathBuf {
  52. get_external_storage_path().join("drk/wallet.db")
  53. }
  54. pub fn get_use_tor_filename() -> PathBuf {
  55. get_external_storage_path().join("use_tor.txt")
  56. }
  57. }
  58. #[cfg(not(target_os = "android"))]
  59. mod paths {
  60. use std::path::PathBuf;
  61. pub fn get_cache_path() -> PathBuf {
  62. dirs::data_local_dir().unwrap().join("darkfi/app/drk/cache")
  63. }
  64. pub fn get_wallet_path() -> PathBuf {
  65. dirs::data_local_dir().unwrap().join("darkfi/app/drk/wallet.db")
  66. }
  67. pub fn get_use_tor_filename() -> PathBuf {
  68. dirs::data_local_dir().unwrap().join("darkfi/app/drk/use_tor.txt")
  69. }
  70. }
  71. use paths::*;
  72. macro_rules! t { ($($arg:tt)*) => { trace!(target: "plugin::drk", $($arg)*); } }
  73. macro_rules! d { ($($arg:tt)*) => { debug!(target: "plugin::drk", $($arg)*); } }
  74. macro_rules! i { ($($arg:tt)*) => { info!(target: "plugin::drk", $($arg)*); } }
  75. macro_rules! e { ($($arg:tt)*) => { error!(target: "plugin::drk", $($arg)*); } }
  76. #[derive(Debug, Clone)]
  77. enum TxStatus {
  78. Confirming,
  79. Confirmed,
  80. Error(String),
  81. }
  82. impl TxStatus {
  83. fn text(&self) -> String {
  84. match self {
  85. TxStatus::Confirming => "Confirming transaction...".to_string(),
  86. TxStatus::Confirmed => "Transaction confirmed".to_string(),
  87. TxStatus::Error(ref err) => format!("Error sending transaction: {err}"),
  88. }
  89. }
  90. }
  91. #[derive(Debug, Clone)]
  92. struct TxState {
  93. id: Option<String>,
  94. status: TxStatus,
  95. amount: Option<String>,
  96. token_symbol: Option<String>,
  97. recipient: Option<Address>,
  98. }
  99. pub type DrkPluginPtr = Arc<DrkPlugin>;
  100. #[derive(Debug, Clone)]
  101. struct BuildTxRequest {
  102. amount: String,
  103. token_id: TokenId,
  104. recipient: PublicKey,
  105. }
  106. pub struct DrkPlugin {
  107. node: SceneNodeWeak,
  108. sg_root: SceneNodePtr,
  109. tasks: OnceLock<Vec<smol::Task<()>>>,
  110. scan_progress_pub: PublisherPtr<(u32, u32)>,
  111. drk: Arc<RwLock<Drk>>,
  112. build_tx_channel: smol::channel::Sender<BuildTxRequest>,
  113. last_balances: SyncMutex<Option<Vec<(String, TokenId, u64)>>>,
  114. }
  115. impl DrkPlugin {
  116. pub async fn new(node: SceneNodeWeak, sg_root: SceneNodePtr, ex: ExecutorPtr) -> Result<Pimpl> {
  117. let node_ref = node.upgrade().unwrap();
  118. let setting_root = Arc::new(SceneNode::new("setting", SceneNodeType::SettingRoot));
  119. node_ref.link(setting_root.clone());
  120. let endpoint = Url::parse(DARKFID_ENDPOINT).unwrap();
  121. let drk = match Drk::new(
  122. Network::Testnet,
  123. get_cache_path().to_string_lossy().to_string(),
  124. get_wallet_path().to_string_lossy().to_string(),
  125. "changeme".to_string(),
  126. Some(endpoint),
  127. &ex,
  128. false,
  129. )
  130. .await
  131. {
  132. Ok(wallet) => wallet,
  133. Err(e) => {
  134. eprintln!("Error initializing wallet: {e}");
  135. return Err(Error::ServiceFailed); // TODO: make a better error
  136. }
  137. };
  138. if let Err(e) = drk.initialize_wallet().await {
  139. e!("Error initializing wallet: {e}");
  140. }
  141. let mut output = vec![];
  142. if let Err(e) = drk.initialize_money(&mut output).await {
  143. e!("Failed to initialize Money: {e}");
  144. }
  145. if let Err(e) = drk.initialize_dao().await {
  146. e!("Failed to initialize DAO: {e}");
  147. }
  148. if let Err(e) = drk.initialize_deployooor().await {
  149. e!("Failed to initialize Deployooor: {e}");
  150. }
  151. // Generate a default address if needed
  152. match drk.default_address().await {
  153. Ok(_) => {
  154. i!("Default address already exists");
  155. }
  156. Err(e) => {
  157. i!("No default address found ({}), generating one...", e);
  158. if let Err(e) = drk.money_keygen(&mut output).await {
  159. e!("Failed to generate keypair: {e}");
  160. } else {
  161. i!("Generated default address");
  162. match drk.addresses().await {
  163. Ok(addrs) => {
  164. if let Some((key_id, _, _, _)) = addrs.last() {
  165. i!("Setting address with key_id {} as default", key_id);
  166. if let Err(e) = drk.set_default_address(*key_id as u16).await {
  167. e!("Failed to set default address: {e}");
  168. }
  169. }
  170. }
  171. Err(e) => {
  172. e!("Failed to get addresses: {e}");
  173. }
  174. }
  175. }
  176. }
  177. }
  178. // Create channel for build_tx requests
  179. let (build_tx_tx, build_tx_rx) = smol::channel::unbounded();
  180. let self_ = Arc::new(Self {
  181. node: node.clone(),
  182. sg_root,
  183. tasks: OnceLock::new(),
  184. drk: drk.into_ptr(),
  185. build_tx_channel: build_tx_tx,
  186. scan_progress_pub: Publisher::new(),
  187. last_balances: SyncMutex::new(None),
  188. });
  189. // Start background task to process build_tx requests from channel
  190. let me3 = Arc::downgrade(&self_);
  191. let build_tx_processor = ex.spawn(async move {
  192. while let Ok(request) = build_tx_rx.recv().await {
  193. if let Some(self_) = me3.upgrade() {
  194. match self_.build_tx_request(request).await {
  195. Ok((tx, token_symbol, recipient, amount)) => {
  196. self_.emit_tx_built(amount, token_symbol, recipient, tx).await;
  197. }
  198. Err(e) => {
  199. e!("Failed to build transaction: {e}");
  200. self_.emit_tx_built_error(e.to_string()).await;
  201. }
  202. }
  203. }
  204. }
  205. });
  206. let node_ref = node.upgrade().unwrap();
  207. let me2 = Arc::downgrade(&self_);
  208. let method_sub = node_ref.subscribe_method_call("get_default_address").unwrap();
  209. let get_address_task =
  210. ex.spawn(
  211. async move { while Self::process_get_default_address(&me2, &method_sub).await {} },
  212. );
  213. let node_ref = node.upgrade().unwrap();
  214. let me2 = Arc::downgrade(&self_);
  215. let method_sub_balances = node_ref.subscribe_method_call("get_balances").unwrap();
  216. let get_balances_task = ex.spawn(async move {
  217. while Self::process_get_balances(&me2, &method_sub_balances).await {}
  218. });
  219. let node_ref = node.upgrade().unwrap();
  220. let me2 = Arc::downgrade(&self_);
  221. let method_sub_tx_status = node_ref.subscribe_method_call("get_tx_status").unwrap();
  222. let get_tx_status_task = ex.spawn(async move {
  223. while Self::process_get_tx_status(&me2, &method_sub_tx_status).await {}
  224. });
  225. let node_ref = node.upgrade().unwrap();
  226. let me2 = Arc::downgrade(&self_);
  227. let method_sub_build_tx = node_ref.subscribe_method_call("build_tx").unwrap();
  228. let build_tx_task =
  229. ex.spawn(
  230. async move { while Self::process_build_tx(&me2, &method_sub_build_tx).await {} },
  231. );
  232. let node_ref = node.upgrade().unwrap();
  233. let me2 = Arc::downgrade(&self_);
  234. let method_sub_broadcast_tx = node_ref.subscribe_method_call("broadcast_tx").unwrap();
  235. let broadcast_tx_task = ex.spawn(async move {
  236. while Self::process_broadcast_tx(&me2, &method_sub_broadcast_tx).await {}
  237. });
  238. let tasks = vec![
  239. get_address_task,
  240. get_balances_task,
  241. get_tx_status_task,
  242. build_tx_task,
  243. broadcast_tx_task,
  244. build_tx_processor,
  245. ];
  246. self_.clone().start(ex.clone(), tasks).await;
  247. Ok(Pimpl::Drk(self_))
  248. }
  249. async fn apply_settings(_self: Arc<Self>, _batch: BatchGuardPtr) {
  250. // TODO
  251. }
  252. pub async fn get_default_address(&self) -> Result<String> {
  253. let drk = self.drk.read().await;
  254. let pubkey = drk.default_address().await.map_err(|e| {
  255. e!("Failed to get default address: {e}");
  256. Error::ServiceFailed
  257. })?;
  258. let network = drk.network;
  259. let address: darkfi_sdk::crypto::keypair::Address =
  260. StandardAddress::from_public(network, pubkey).into();
  261. Ok(address.to_string())
  262. }
  263. pub async fn get_balances(&self) -> Result<Vec<(String, TokenId, u64)>> {
  264. let drk = self.drk.read().await;
  265. let balances = drk.money_balance().await.map_err(|e| {
  266. e!("Failed to get money balance: {e}");
  267. Error::ServiceFailed
  268. })?;
  269. let aliases = drk.get_aliases_mapped_by_token().await.map_err(|e| {
  270. e!("Failed to get aliases: {e}");
  271. Error::ServiceFailed
  272. })?;
  273. let mut result: Vec<(String, TokenId, u64)> = Vec::new();
  274. for (token_id_str, balance) in balances {
  275. let alias = aliases.get(&token_id_str).cloned().unwrap_or_else(|| "UNKN".to_string());
  276. let token_id = token_id_str.parse::<TokenId>().unwrap();
  277. result.push((alias, token_id, balance));
  278. }
  279. // Sort by balance
  280. result.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
  281. Ok(result)
  282. }
  283. /// Emit balances_updated signal with the balances encoded in the payload.
  284. /// Only emits when the encoded balances differ from the last emitted ones.
  285. async fn emit_balances_updated(&self) {
  286. let Some(node) = self.node.upgrade() else { return };
  287. let balances = match self.get_balances().await {
  288. Ok(b) => b,
  289. Err(e) => {
  290. e!("Failed to get balances for balances_updated signal: {e}");
  291. return
  292. }
  293. };
  294. let mut data = vec![];
  295. if let Err(e) = balances.encode(&mut data) {
  296. e!("Failed to encode balances for balances_updated signal: {e}");
  297. return
  298. }
  299. let mut last = self.last_balances.lock();
  300. if let Some(last) = &*last {
  301. if *last == balances {
  302. return
  303. }
  304. }
  305. *last = Some(balances);
  306. let _ = node.trigger("balances_updated", data).await;
  307. }
  308. /// Emit tx_updated signal
  309. async fn emit_tx_updated(&self, state: &TxState) {
  310. if let Some(node) = self.node.upgrade() {
  311. let mut data = vec![];
  312. state.id.clone().encode(&mut data).unwrap();
  313. Some(state.status.text()).encode(&mut data).unwrap();
  314. state.amount.encode(&mut data).unwrap();
  315. state.token_symbol.clone().encode(&mut data).unwrap();
  316. state.recipient.map(|r| r.to_string()).encode(&mut data).unwrap();
  317. let _ = node.trigger("tx_updated", data).await;
  318. }
  319. }
  320. async fn emit_tx_status_updated(&self, status: &TxStatus) {
  321. if let Some(node) = self.node.upgrade() {
  322. let mut data = vec![];
  323. None::<String>.encode(&mut data).unwrap();
  324. Some(status.text()).encode(&mut data).unwrap();
  325. None::<String>.encode(&mut data).unwrap();
  326. None::<String>.encode(&mut data).unwrap();
  327. None::<String>.encode(&mut data).unwrap();
  328. let _ = node.trigger("tx_updated", data).await;
  329. }
  330. }
  331. /// Emit tx_built signal when transaction is built
  332. async fn emit_tx_built(
  333. &self,
  334. amount: String,
  335. token_symbol: String,
  336. recipient: Address,
  337. tx: Transaction,
  338. ) {
  339. if let Some(node) = self.node.upgrade() {
  340. let mut data = vec![];
  341. amount.encode(&mut data).unwrap();
  342. token_symbol.encode(&mut data).unwrap();
  343. recipient.to_string().encode(&mut data).unwrap();
  344. tx.encode(&mut data).unwrap();
  345. let _ = node.trigger("tx_built", data).await;
  346. }
  347. }
  348. /// Emit tx_built_error signal when transaction building fails
  349. async fn emit_tx_built_error(&self, error: String) {
  350. if let Some(node) = self.node.upgrade() {
  351. let mut data = vec![];
  352. error.encode(&mut data).unwrap();
  353. let _ = node.trigger("tx_built_error", data).await;
  354. }
  355. }
  356. async fn process_get_default_address(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  357. let Ok(method_call) = sub.receive().await else {
  358. d!("get_default_address method closed");
  359. return false
  360. };
  361. t!("method called: get_default_address()");
  362. let Some(self_) = me.upgrade() else {
  363. e!("drk plugin destroyed before get_default_address task was stopped!");
  364. if let Some(send_res) = method_call.send_res {
  365. let _ = send_res.send(vec![]).await;
  366. }
  367. return false
  368. };
  369. let address = match self_.get_default_address().await {
  370. Ok(addr) => addr,
  371. Err(e) => {
  372. e!("Failed to get default address: {e}");
  373. if let Some(send_res) = method_call.send_res {
  374. let _ = send_res.send(vec![]).await;
  375. }
  376. return true
  377. }
  378. };
  379. i!("Got default address: {address}");
  380. if let Some(send_res) = method_call.send_res {
  381. let mut cur = Cursor::new(vec![]);
  382. if address.encode(&mut cur).is_ok() {
  383. let _ = send_res.send(cur.into_inner()).await;
  384. } else {
  385. e!("Failed to encode default address");
  386. let _ = send_res.send(vec![]).await;
  387. }
  388. } else {
  389. e!("No send_res channel available");
  390. }
  391. true
  392. }
  393. async fn process_get_balances(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  394. let Ok(method_call) = sub.receive().await else {
  395. d!("get_balances method closed");
  396. return false
  397. };
  398. t!("method called: get_balances()");
  399. let Some(self_) = me.upgrade() else {
  400. e!("drk plugin destroyed before get_balances task was stopped!");
  401. if let Some(send_res) = method_call.send_res {
  402. let _ = send_res.send(vec![]).await;
  403. }
  404. return false
  405. };
  406. let balances = match self_.get_balances().await {
  407. Ok(b) => b,
  408. Err(e) => {
  409. e!("Failed to get balances: {e}");
  410. if let Some(send_res) = method_call.send_res {
  411. let _ = send_res.send(vec![]).await;
  412. }
  413. return true
  414. }
  415. };
  416. if let Some(send_res) = method_call.send_res {
  417. let mut cur = Cursor::new(vec![]);
  418. if balances.encode(&mut cur).is_ok() {
  419. let _ = send_res.send(cur.into_inner()).await;
  420. } else {
  421. e!("Failed to encode balances");
  422. let _ = send_res.send(vec![]).await;
  423. }
  424. } else {
  425. e!("No send_res channel available");
  426. }
  427. true
  428. }
  429. async fn process_get_tx_status(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  430. let Ok(method_call) = sub.receive().await else {
  431. d!("get_tx_history method closed");
  432. return false
  433. };
  434. t!("method called: get_tx_status()");
  435. fn decode_data(data: &[u8]) -> std::io::Result<String> {
  436. let mut cur = Cursor::new(&data);
  437. let tx_id = String::decode(&mut cur)?;
  438. Ok(tx_id)
  439. }
  440. let Ok(tx_id) = decode_data(&method_call.data) else {
  441. d!("get_tx_status() method invalid arg data");
  442. return true
  443. };
  444. let Some(self_) = me.upgrade() else {
  445. e!("drk plugin destroyed before get_tx_status task was stopped!");
  446. if let Some(send_res) = method_call.send_res {
  447. let _ = send_res.send(vec![]).await;
  448. }
  449. return false
  450. };
  451. let drk = self_.drk.read().await;
  452. let Ok((_, status, _block_height, _tx)) = drk.get_tx_history_record(&tx_id).await else {
  453. d!("get_tx_history() method failed to get tx history record");
  454. return true
  455. };
  456. if let Some(send_res) = method_call.send_res {
  457. let mut cur = Cursor::new(vec![]);
  458. let status = match status.as_str() {
  459. "Broadcasted" => TxStatus::Confirming,
  460. "Confirmed" => TxStatus::Confirmed,
  461. _ => TxStatus::Error("unknown status".to_string()),
  462. };
  463. if status.text().encode(&mut cur).is_ok() {
  464. let _ = send_res.send(cur.into_inner()).await;
  465. } else {
  466. e!("Failed to encode balances");
  467. let _ = send_res.send(vec![]).await;
  468. }
  469. } else {
  470. e!("No send_res channel available");
  471. }
  472. true
  473. }
  474. /// Build a transaction without broadcasting it
  475. pub async fn build_tx(
  476. &self,
  477. amount: &str,
  478. token_id: TokenId,
  479. recipient: PublicKey,
  480. ) -> DarkFiResult<Transaction> {
  481. let drk = self.drk.read().await;
  482. drk.transfer(amount, token_id, recipient, None, None, false).await
  483. }
  484. /// Build a transaction from a BuildTxRequest (called by background task)
  485. async fn build_tx_request(
  486. &self,
  487. request: BuildTxRequest,
  488. ) -> DarkFiResult<(Transaction, String, Address, String)> {
  489. let drk = self.drk.read().await;
  490. let aliases = drk.get_aliases_mapped_by_token().await.unwrap_or_default();
  491. let token_symbol =
  492. aliases.get(&request.token_id.to_string()).unwrap_or(&"UNKN".to_string()).to_string();
  493. let recipient: Address =
  494. StandardAddress::from_public(drk.network, request.recipient).into();
  495. let tx = self.build_tx(&request.amount, request.token_id, request.recipient).await?;
  496. Ok((tx, token_symbol, recipient, request.amount))
  497. }
  498. async fn process_build_tx(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  499. let Ok(method_call) = sub.receive().await else {
  500. d!("build_tx method closed");
  501. return false
  502. };
  503. t!("method called: build_tx()");
  504. // Send empty response immediately to unblock the caller
  505. if let Some(send_res) = method_call.send_res {
  506. let _ = send_res.send(vec![]).await;
  507. }
  508. fn decode_data(data: &[u8]) -> std::io::Result<(String, TokenId, PublicKey)> {
  509. let mut cur = Cursor::new(&data);
  510. let amount = String::decode(&mut cur)?;
  511. let token_id = TokenId::decode(&mut cur)?;
  512. let recipient = PublicKey::decode(&mut cur)?;
  513. Ok((amount, token_id, recipient))
  514. }
  515. let Ok((amount, token_id, recipient)) = decode_data(&method_call.data) else {
  516. d!("build_tx() method invalid arg data");
  517. return true
  518. };
  519. let Some(self_) = me.upgrade() else {
  520. e!("drk plugin destroyed before build_tx task was stopped!");
  521. return false
  522. };
  523. // Send request to channel for background processing
  524. let request = BuildTxRequest { amount, token_id, recipient };
  525. let _ = self_.build_tx_channel.send(request).await;
  526. true
  527. }
  528. /// Broadcast a transaction
  529. pub async fn broadcast_tx(&self, tx: Transaction) -> Result<String> {
  530. let drk = self.drk.read().await;
  531. let tx_id = drk.broadcast_tx(&tx, &mut vec![]).await.map_err(|e| {
  532. e!("Failed to broadcast transaction: {e}");
  533. Error::ServiceFailed
  534. })?;
  535. Ok(tx_id)
  536. }
  537. async fn process_broadcast_tx(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  538. let Ok(method_call) = sub.receive().await else {
  539. d!("broadcast_tx method closed");
  540. return false
  541. };
  542. t!("method called: broadcast_tx()");
  543. let Some(send_res) = method_call.send_res else { return true };
  544. // Send empty response immediately to unblock the caller
  545. let _ = send_res.send(vec![]).await;
  546. let Some(self_) = me.upgrade() else {
  547. e!("drk plugin destroyed before broadcast_tx task was stopped!");
  548. return false
  549. };
  550. let Ok(tx) = Transaction::decode(&mut Cursor::new(&method_call.data)) else {
  551. d!("broadcast_tx() method invalid arg data");
  552. return true
  553. };
  554. let drk = self_.drk.read().await;
  555. if let Err(e) = drk.mark_tx_spend(&tx, &mut vec![]).await {
  556. e!("Failed to mark transaction coins as spent: {e}");
  557. self_
  558. .emit_tx_status_updated(&TxStatus::Error(
  559. "failed to mark coins as spent".to_string(),
  560. ))
  561. .await;
  562. return true
  563. };
  564. let tx_id = match drk.broadcast_tx(&tx, &mut vec![]).await {
  565. Ok(t) => t,
  566. Err(e) => {
  567. e!("Failed to broadcast transaction: {e}");
  568. self_
  569. .emit_tx_status_updated(&TxStatus::Error("failed to broadcast".to_string()))
  570. .await;
  571. return true
  572. }
  573. };
  574. drop(drk);
  575. let state = TxState {
  576. id: Some(tx_id),
  577. status: TxStatus::Confirming,
  578. amount: None,
  579. token_symbol: None,
  580. recipient: None,
  581. };
  582. self_.emit_tx_updated(&state).await;
  583. self_.emit_balances_updated().await;
  584. true
  585. }
  586. async fn start(self: Arc<Self>, ex: ExecutorPtr, tasks: Vec<smol::Task<()>>) {
  587. let endpoint = Url::parse(DARKFID_ENDPOINT).unwrap();
  588. let self2 = self.clone();
  589. let drk = self.drk.clone();
  590. let (shell_sender, shell_receiver) = unbounded();
  591. let ex_ = ex.clone();
  592. let progress_sub = self.scan_progress_pub.clone().subscribe().await;
  593. let scan_progress_task = ex.spawn(async move {
  594. let mut first_height = None;
  595. loop {
  596. let (height, final_height) = progress_sub.receive().await;
  597. if first_height.is_none() {
  598. first_height = Some(height);
  599. }
  600. let progress: f64 = match final_height - first_height.unwrap() {
  601. 0 => 0.,
  602. _ => {
  603. (height - first_height.unwrap()) as f64 /
  604. (final_height - first_height.unwrap()) as f64
  605. }
  606. };
  607. let status: u8 = if progress > 0.5 { 2 } else { 1 };
  608. let Some(node) = self2.node.upgrade() else { continue };
  609. let start_height = first_height.unwrap();
  610. let blocks_scanned = height - start_height;
  611. let total_blocks = final_height - start_height;
  612. let percentage = if total_blocks > 0 {
  613. (blocks_scanned as f32 / total_blocks as f32 * 100.0) as u32
  614. } else {
  615. 0
  616. };
  617. let desc = format!("{}/{} [{}%]", blocks_scanned, total_blocks, percentage);
  618. let _ = node.trigger("connect", serialize(&(status, desc))).await;
  619. }
  620. });
  621. let self2 = self.clone();
  622. // Task that handles the RPC subscription with retry logic
  623. let subscribe_task = ex.spawn(async move {
  624. loop {
  625. i!("Attempting to connect to darkfid daemon at {}", endpoint);
  626. let subscribe_rpc_task = StoppableTask::new();
  627. let shell_sender = shell_sender.clone();
  628. let drk = drk.clone();
  629. let endpoint = endpoint.clone();
  630. let ex = ex_.clone();
  631. let progress_pub = self2.scan_progress_pub.clone();
  632. let _ = self2
  633. .node
  634. .upgrade()
  635. .unwrap()
  636. .trigger("connect", serialize(&(0u8, String::new())))
  637. .await;
  638. if let Err(e) = drk
  639. .read()
  640. .await
  641. .scan_blocks(&mut vec![], Some(&shell_sender), &false, Some(progress_pub))
  642. .await
  643. {
  644. e!("Failed during drk scanning: {e}");
  645. let _ = self2
  646. .node
  647. .upgrade()
  648. .unwrap()
  649. .trigger("connect", serialize(&(0u8, String::new())))
  650. .await;
  651. // Wait before retrying
  652. i!("Retrying connection to darkfid in {} seconds...", DARKFID_RETRY_TIME);
  653. sleep(DARKFID_RETRY_TIME).await;
  654. continue
  655. }
  656. let _ = self2
  657. .node
  658. .upgrade()
  659. .unwrap()
  660. .trigger("connect", serialize(&(3u8, String::new())))
  661. .await;
  662. self2.emit_balances_updated().await;
  663. match subscribe_blocks(
  664. &drk,
  665. subscribe_rpc_task,
  666. shell_sender.clone(),
  667. endpoint,
  668. &ex,
  669. )
  670. .await
  671. {
  672. Ok(()) => {
  673. i!("darkfid subscription closed normally (detached task stopped)");
  674. }
  675. Err(e) => {
  676. e!("darkfid connection failed: {e}");
  677. }
  678. }
  679. let _ = self2
  680. .node
  681. .upgrade()
  682. .unwrap()
  683. .trigger("connect", serialize(&(0u8, String::new())))
  684. .await;
  685. // Wait before retrying
  686. i!("Retrying connection to darkfid in {} seconds...", DARKFID_RETRY_TIME);
  687. sleep(DARKFID_RETRY_TIME).await;
  688. }
  689. });
  690. let self2 = self.clone();
  691. let subscribe_recv_task = ex.spawn(async move {
  692. loop {
  693. let recv = shell_receiver.recv().await;
  694. if let Ok(lines) = recv {
  695. self2.emit_balances_updated().await;
  696. for line in lines.iter() {
  697. i!(line);
  698. }
  699. }
  700. }
  701. });
  702. let mut all_tasks = vec![scan_progress_task, subscribe_task, subscribe_recv_task];
  703. all_tasks.extend(tasks);
  704. self.tasks.set(all_tasks).unwrap();
  705. }
  706. }